Compare commits

..

271 Commits

Author SHA1 Message Date
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
160 changed files with 18994 additions and 5274 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,19 @@ 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 ><div className="dot-spinner">
thickness='3px' <div className="dot-spinner__dot"></div>
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>
{/* <Text color='#004717' fontSize={'md'} fontWeight={500}>Loading...</Text> */}
</Box> </Box>
); );
}; };

View File

@@ -1,12 +1,18 @@
import React from "react"; import React from "react";
import './FullscreenLoaders.css'
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 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> </div>
); );
}; };

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,140 @@ 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 [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 +1634,16 @@ const GlobalStateProvider = ({ children }) => {
setAcademicDocuments, setAcademicDocuments,
iOArtifactsTwo, iOArtifactsTwo,
setIOArtifactsTwo, setIOArtifactsTwo,
ioStatus, InvestorWallet,
setIoStatus, setInvestorWallet,
isIOloading,
setIOloading,
users,
setUsers,
fawateerRequest,
setFawateerRequest,
approveHistory,
setApproveHistory
}} }}
> >
{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 comments...."}
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,242 @@
import { import {
Box, Badge,
Button, Box,
FormControl, Button,
FormLabel, FormControl,
Input, FormErrorMessage,
Modal, FormLabel,
ModalBody, Input,
ModalCloseButton, Modal,
ModalContent, ModalBody,
ModalFooter, ModalCloseButton,
ModalHeader, ModalContent,
ModalOverlay, ModalFooter,
Text, ModalHeader,
useDisclosure, ModalOverlay,
} from "@chakra-ui/react"; Text,
import React from "react"; Textarea,
import * as yup from "yup"; useDisclosure,
import { yupResolver } from "@hookform/resolvers/yup"; useToast,
import { useForm } from "react-hook-form"; } from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
export const conformModalSchema = yup.object().shape({ import * as yup from "yup";
fees: yup.string().required("File name is required"), import { yupResolver } from "@hookform/resolvers/yup";
totalAmount: yup.string().required("File name is required"), 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(),
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">Comments</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 comments...."}
resize={"none"}
/> />
{errors.totalAmount && ( {errors.comment && (
<Text fontSize="xs" color="red"> <Text fontSize="xs" color="red">
{errors.totalAmount.message} {errors.comment.message}
</Text> </Text>
)} )}
</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,163 @@
import { import {
Box, Box,
Button, Button,
FormControl, FormControl,
FormLabel, FormLabel,
Input, Input,
Modal, Modal,
ModalBody, ModalBody,
ModalCloseButton, ModalCloseButton,
ModalContent, ModalContent,
ModalFooter, ModalFooter,
ModalHeader, ModalHeader,
ModalOverlay, ModalOverlay,
Text, Text,
Textarea, Textarea,
useDisclosure, useDisclosure,
} from "@chakra-ui/react"; useToast,
import React from "react"; } from "@chakra-ui/react";
import * as yup from "yup"; import React, { useEffect, useState } from "react";
import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup";
import { useForm } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
export const conformModalSchema = yup.object().shape({ import { useDepositRejectMutation } from "../../../Services/deposit.request.service";
comment: yup.string().required("Comment is required"), import ToastBox from "../../../Components/ToastBox";
export const conformModalSchema = yup.object().shape({
comments: yup.string().required("Comment is required"),
});
const DepositRequestReject = ({ 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 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 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 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,313 @@
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(),
});
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, 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().toISOString().split("T")[0]} // Disable future dates
{...register("transaction_date")}
/>
<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")}
/>
<FormHelperText fontSize={'xs'} fontWeight={500} style={{ color: "red" }}>{errors.makerComment?.message}</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);
}
};
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

@@ -15,13 +15,12 @@ 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";
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 +42,38 @@ 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,
}, },
]; ];
@@ -86,19 +91,23 @@ 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) => (

View File

@@ -1,9 +1,228 @@
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 Distribution = () => { const Destribution = () => {
return ( const { navDetails, setNavDetails, IODetails } =
<div>Distribution</div> 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>
);
};
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
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 Distribution 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="error" />,
});
} }
} 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="error" />,
});
} }
} 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

@@ -27,24 +27,27 @@ 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"), 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 +66,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 +74,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 +81,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 +130,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}
@@ -200,7 +206,7 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, d
</DrawerContent> </DrawerContent>
</Drawer> </Drawer>
<CustomAlertDialog <CustomAlertDialog
isOpen={alert} isOpen={alert}
onClose={() => setAlert(false)} onClose={() => setAlert(false)}

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 IOCashDetails = () => {
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,54 @@ 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 +218,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}
@@ -260,18 +312,45 @@ const IOCashDetails = () => {
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
/> />
<HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} /> <HStack display={"flex"} alignItems={"center"}>
</HStack> <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>
</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,6 +366,12 @@ const IOCashDetails = () => {
alertHandler={handleDelete} alertHandler={handleDelete}
isLoading={isLoading} isLoading={isLoading}
/> />
<AddCashDetails
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
</Box> </Box>
); );
}; };

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()
@@ -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,22 +1,44 @@
import React, { useContext, useEffect, useState } from 'react' import React, { useContext, useEffect, useRef, useState } from "react";
import GlobalStateContext from '../../../Contexts/GlobalStateContext'; import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import { Box, HStack, Input,Text, Table, Tbody, Th, Tr } from '@chakra-ui/react'; import {
import { OPACITY_ON_LOAD } from '../../../Layout/animations'; Box,
import Pagination from '../../../Components/Pagination'; HStack,
import DataTable from '../../../Components/DataTable/DataTable'; Input,
import CustomAlertDialog from '../../../Components/CustomAlertDialog'; 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 IONAVDetails = () => {
const { navDetails, setNavDetails, slideFromRight } = const { navDetails, setNavDetails, IODetails } =
useContext(GlobalStateContext); useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState(""); const firstField = useRef();
const [isLoading, setIsLoading] = useState(true); const { isOpen, onOpen, onClose } = useDisclosure();
const [deleteAlert, setDeleteAlert] = useState(false); const [searchTerm, setSearchTerm] = useState("");
const [actionId, setActionId] = useState(false); const [isLoading, setIsLoading] = useState(true);
const [mouseEntered, setMouseEntered] = useState(false); const [deleteAlert, setDeleteAlert] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState(""); const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
console.log(IODetails?.ioNAVHistory);
useEffect(() => { useEffect(() => {
// Simulate loading // Simulate loading
const timer = setTimeout(() => { const timer = setTimeout(() => {
@@ -27,90 +49,163 @@ const IONAVDetails = () => {
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, []); }, []);
// Table setup // Table setup
const tableHeadRow = [ const tableHeadRow = [
"Sr.No", // "Sr.No",
"As On Date", "Valuation Date",
"IO NAV Value", "NAV",
"Comments", "Last NAV update",
"Update by ", "Investment Closed",
"Update On", "Comments",
]; "Update by ",
// "Update On",
];
// Table filter // Table filter
const filteredData = navDetails?.filter((item) => { const filteredData = IODetails?.ioNAVHistory
const name = item.updateBy; ?.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;
})
.reverse()
// .sort((b, a) => new Date(a.transactionDate) - new Date(b.transactionDate));
const [ extractedArray, setExtractedArray ] = useState(filteredData?.map((item, index) => ({ const extractedArray = filteredData?.map((item, index) => ({
id: item?.id, id: item?.id,
"Sr.No": index +1, "Sr.No": index + 1,
"As On Date": ( "Valuation 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} {/* {/ {formatDatee(item.transactionDate)} /} */}
{formatDate(item?.transactionDate)}
</Text> </Text>
), ),
"IO NAV Value": ( NAV: (
<Text <Text
justifyContent={slideFromRight ? "right" : "center"} 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"} 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.IONavValue}`} {item.previousNAVvalue && `${item.previousNAVvalue}`}
</Text> </Text>
), ),
"Comments": ( "Investment Closed": (
<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.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} {item.comments}
</Text> </Text>
), ),
"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: "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 handleDelete = () => {
const updatedNav = navDetails.filter( const updatedNav = navDetails.filter((sponsor) => sponsor.id !== actionId);
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => { setTimeout(() => {
setNavDetails(updatedNav); setNavDetails(updatedNav);
@@ -120,57 +215,84 @@ const IONAVDetails = () => {
setIsLoading(true); 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>
return (<Box {...OPACITY_ON_LOAD} pb={0}> {IODetails?.isInvestedAmount ? (
<Box bg="white.500"> <Button
<HStack onClick={onOpen}
display={"flex"} leftIcon={<AddIcon />}
justifyContent={"space-between"} colorScheme="forestGreen"
pb={3} size={"sm"}
spacing="24px" rounded={"sm"}
> fontSize={"xs"}
<Input >
type="search" Add IO Nav
width={300} </Button>
placeholder="Search..." ) : null}
size="sm" </HStack>
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} />
</HStack> </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> </Box>
);
};
<DataTable export default IONAVDetails;
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

@@ -33,12 +33,12 @@ import {
import ToastBox from "../../../Components/ToastBox"; import ToastBox from "../../../Components/ToastBox";
import { getFileNameFromPath } from "../../../Constants/Constants"; import { getFileNameFromPath } from "../../../Constants/Constants";
import { TbFileTypeDocx } from "react-icons/tb"; import { TbFileTypeDocx } from "react-icons/tb";
import SetDisplayOrder from "./SetDisplayOrder"; import SetDisplayOrder from "./SetDisplayOrderKeyMerits";
import SetDisplayOrderIODocuments from "./SetDisplayOrderIODocuments";
const downloadFile = (filePath, fileName) => { const downloadFile = (filePath, fileName) => {
console.log("https://tanami.betadelivery.com/" + filePath);
fetch("https://tanami.betadelivery.com/" + filePath) fetch(import.meta.env.VITE_IMAGE_URL+filePath)
.then((response) => { .then((response) => {
if (!response.ok) { if (!response.ok) {
throw new Error("Network response was not ok"); throw new Error("Network response was not ok");
@@ -72,10 +72,10 @@ const downloadFile = (filePath, fileName) => {
}); });
}; };
const InvestmentDocument = ({ control, errors, enableNextTab, index }) => { const InvestmentDocument = ({ control, errors, enableNextTab, index, }) => {
const params = useParams(); const params = useParams();
const id = params?.id; const id = params?.id;
const { slideFromRight, create, setCreate } = useContext(GlobalStateContext); const { slideFromRight, create, setCreate, IODetails } = useContext(GlobalStateContext);
const firstField = useRef(); const firstField = useRef();
const secondField = useRef(); const secondField = useRef();
const thirdField = useRef(); const thirdField = useRef();
@@ -101,13 +101,18 @@ const InvestmentDocument = ({ control, errors, enableNextTab, index }) => {
const [deleteIODocs] = useDeleteIODocsMutation(); const [deleteIODocs] = useDeleteIODocsMutation();
const { // const {
data, // data,
error, // error,
isLoading: isIODocLoading, // isLoading: isIODocLoading,
} = useGetInvestmentDocumentsQuery(id, { // } = useGetInvestmentDocumentsQuery(id, {
skip: !id, // skip: !id,
}); // });
const tableHeadRow = ["Sr.no", "Type", "File Name", "Document", "Action"]; const tableHeadRow = ["Sr.no", "Type", "File Name", "Document", "Action"];
@@ -126,10 +131,14 @@ const InvestmentDocument = ({ control, errors, enableNextTab, index }) => {
}); });
}, 300); }, 300);
const filteredData = data?.data?.filter((item) => const filteredData = IODetails?.documents?.filter((item) =>
item?.documentName?.toLowerCase().includes(searchTerm.toLowerCase()) item?.documentName?.toLowerCase().includes(searchTerm.toLowerCase())
); );
const sortedData = filteredData?.sort(
(a, b) => a.displayOrder - b.displayOrder
);
const handleView = (id) => { const handleView = (id) => {
setActionId(id); setActionId(id);
onViewOpen(); onViewOpen();
@@ -164,7 +173,7 @@ const InvestmentDocument = ({ control, errors, enableNextTab, index }) => {
} }
}; };
const extractedArray = filteredData?.map((item, index) => ({ const extractedArray = sortedData?.map((item, index) => ({
"Sr.no": ( "Sr.no": (
<Text <Text
justifyContent={slideFromRight ? "right" : "left"} justifyContent={slideFromRight ? "right" : "left"}
@@ -294,7 +303,9 @@ const InvestmentDocument = ({ control, errors, enableNextTab, index }) => {
return ( return (
<Box> <Box>
<Box display="flex" justifyContent="end" mb={4} gap={2}> <Box display="flex" justifyContent="end" mb={4} gap={2}>
<SetDisplayOrder data={filteredData} />
{filteredData?.length !== 0 &&<SetDisplayOrderIODocuments data={filteredData} />}
<Button <Button
leftIcon={<AddIcon />} leftIcon={<AddIcon />}
onClick={onOpen} onClick={onOpen}
@@ -319,7 +330,7 @@ const InvestmentDocument = ({ control, errors, enableNextTab, index }) => {
secondField={secondField} secondField={secondField}
/> />
<InvestmentEdit <InvestmentEdit
data={data?.data} data={IODetails?.documents}
id={actionId} id={actionId}
isOpen={isEditOpen} isOpen={isEditOpen}
onClose={onEditClose} onClose={onEditClose}

View File

@@ -4,6 +4,7 @@ import {
Box, Box,
Button, Button,
HStack, HStack,
Icon,
Input, Input,
Menu, Menu,
MenuButton, MenuButton,
@@ -20,25 +21,50 @@ import {
Thead, Thead,
Tooltip, Tooltip,
Tr, Tr,
keyframes,
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 { HiDotsVertical } from "react-icons/hi"; import { HiDotsVertical } from "react-icons/hi";
import { Link, Link as RouterLink } from "react-router-dom"; import { Link, Link as RouterLink, useParams } from "react-router-dom";
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 { formatCurrency } from "../../../Components/CurrencyInput";
import { FiRefreshCw } from "react-icons/fi";
import { useGetIOByIdQuery } from "../../../Services/io.service";
import { RepeatIcon } from "@chakra-ui/icons";
import { exportToExcel, exportToExcelNew } from "../../../Constants/Constants";
import { LuFileSpreadsheet } from "react-icons/lu";
const rotate = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`;
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
const Investors = () => { const Investors = ({ data }) => {
const params = useParams();
const id = params?.id;
const toast = useToast(); const toast = useToast();
const { investors, setInvestors, slideFromRight } = const { investors, setInvestors, slideFromRight, IODetails } =
useContext(GlobalStateContext); useContext(GlobalStateContext);
const [isRefetchLoading, setIsRefetchLoading] = useState(false);
const { isLoading: IObyIDisLoading, refetch } = useGetIOByIdQuery(id, {
skip: !id,
});
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false); const [deleteAlert, setDeleteAlert] = useState(false);
@@ -84,6 +110,7 @@ const Investors = () => {
"Market Value", "Market Value",
"Return on Investment", "Return on Investment",
"Distribution", "Distribution",
"Distribution Percent",
"Total Return", "Total Return",
"Total return on Investment", "Total return on Investment",
]; ];
@@ -99,16 +126,50 @@ const Investors = () => {
}); });
}, 300); }, 300);
// Table filter // Table filter
const filteredData = investors.filter((item) => { const filteredData = IODetails?.investors?.filter((item) => {
const clientId = item?.clientReference_id;
const name = item.firstName; const name = item.firstName;
const lastName = item?.lastName
const searchLower = searchTerm.toLowerCase(); const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower); const nameMatches = name.toLowerCase().includes(searchLower) || lastName.toLowerCase().includes(searchLower) || clientId.toLowerCase().includes(searchLower);
return nameMatches; return nameMatches;
}); });
const [ extractedArray, setExtractedArray ] = useState(filteredData?.map((item, index) => ({ const customHeaders = [
{ label: "Client ID", key: "clientReference_id" },
{ label: "First Name", key: "firstName" }, // Nested property
{ label: "Last Name", key: "lastName" }, // Nested property
{ label: "Investment amount", key: "InvestedAmount_USD" }, // Nested property
{ label: "Percentage", key: "Investor_Holidings" }, // Nested property
{ label: "Market Value", key: "Market_Value" }, // Nested property
{ label: "Return on Investment", key: "Return_On_Investment" }, // Nested property
{ label: "Distribution", key: "Distribution_Amt" }, // Simple property
{ label: "Distribution Percent", key: "Distribution_Per" }, // Simple property // Simple property
{ label: "Total Return", key: "Total_Return" }, // Simple property
{ label: "Total return on Investment", key: "Total_Return_On_Investment" },
];
const exportInvestorDetails = IODetails?.investors?.map((item, index) => ({
"Client ID": item?.clientReference_id, // Keep as string
"First Name": item?.firstName,
"Last Name": item?.lastName,
"Investment Amount": parseFloat(item?.InvestedAmount_USD) || 0, // Convert to float
"Percentage": parseFloat(item?.Investor_Holidings) || 0, // Convert to float
"Market Value": parseFloat(item?.Market_Value) || 0, // Convert to float
"Return on Investment": parseFloat(item?.Return_On_Investment) || 0, // Convert to float
"Distribution": parseFloat(item?.Distribution_Amt) || 0, // Convert to float
"Distribution Percent": parseFloat(item?.Distribution_Per) || 0, // Convert to float
"Total Return": parseFloat(item?.Total_Return) || 0, // Convert to float
"Total return on Investment": parseFloat(item?.Total_Return_On_Investment) || 0, // Convert to float
}));
console.log(exportInvestorDetails);
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id, id: item?.id,
"Client ID": ( "Client ID": (
<Text <Text
@@ -118,7 +179,7 @@ const Investors = () => {
fontWeight={"500"} fontWeight={"500"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
> >
{item.clientId} {item?.clientReference_id}
</Text> </Text>
), ),
"First name": ( "First name": (
@@ -145,16 +206,23 @@ const Investors = () => {
), ),
"Investment amount": ( "Investment amount": (
<Text <Text
justifyContent={slideFromRight ? "right" : "center"} 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"
> >
{`$${item.investedAmount}`} <Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{/* {`$${formatCurrency(item.InvestedAmount_USD)}`} */}
{`${parseFloat(item.InvestedAmount_USD || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text> </Text>
), ),
"Percentage": ( Percentage: (
<Text <Text
justifyContent={slideFromRight ? "right" : "center"} justifyContent={slideFromRight ? "right" : "center"}
as={"span"} as={"span"}
@@ -162,18 +230,24 @@ const Investors = () => {
fontWeight={"500"} fontWeight={"500"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
> >
{item.percentage} {item.Investor_Holidings} %
</Text> </Text>
), ),
"Market Value": ( "Market Value": (
<Text <Text
justifyContent={slideFromRight ? "right" : "center"} 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"
> >
{`$${item.marketValue}`} <Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{`${parseFloat(item.Market_Value || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text> </Text>
), ),
"Return on Investment": ( "Return on Investment": (
@@ -185,10 +259,28 @@ const Investors = () => {
h={6} h={6}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
> >
{item.returnOnInvestment} {item.Return_On_Investment || 0} %
</Text> </Text>
), ),
"Distribution": ( Distribution: (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{/* {`$${item.Distribution_Amt}`} */}
{`${parseFloat(item.Distribution_Amt || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text>
),
"Distribution Percent": (
<Text <Text
justifyContent={slideFromRight ? "right" : "center"} justifyContent={slideFromRight ? "right" : "center"}
as={"span"} as={"span"}
@@ -196,18 +288,29 @@ const Investors = () => {
fontWeight={"500"} fontWeight={"500"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
> >
{`$${item.distribution}`} {/* {`$${item.Distribution_Amt}`} */}
{`${parseFloat(item.Distribution_Per || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})} %`}
</Text> </Text>
), ),
"Total Return": ( "Total Return": (
<Text <Text
justifyContent={slideFromRight ? "right" : "center"} 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"
> >
{`$${item.totalReturn}`} <Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{/* {`$${formatCurrency(item.Total_Return) || 0}`} */}
{`${parseFloat(item.Total_Return || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text> </Text>
), ),
"Total return on Investment": ( "Total return on Investment": (
@@ -218,10 +321,10 @@ const Investors = () => {
fontWeight={"500"} fontWeight={"500"}
className="d-flex align-items-center web-text-small" className="d-flex align-items-center web-text-small"
> >
{item.totalReturnOnInvestment} {item.Total_Return_On_Investment || 0} %
</Text> </Text>
), ),
}))); }));
const handleDelete = () => { const handleDelete = () => {
const updatedSponsors = sponser.filter( const updatedSponsors = sponser.filter(
@@ -240,7 +343,7 @@ const Investors = () => {
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}
@@ -357,6 +460,17 @@ const Investors = () => {
); );
}; };
const handleRefresh = async () => {
setIsRefetchLoading(true);
await refetch();
setIsRefetchLoading(false);
};
console.log(IODetails?.investors);
return ( return (
<Box {...OPACITY_ON_LOAD} pb={0}> <Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500"> <Box bg="white.500">
@@ -366,29 +480,90 @@ const Investors = () => {
pb={3} pb={3}
spacing="24px" spacing="24px"
> >
<Input <Box display={'flex'} gap={3}>
type="search" <Input
width={300} type="search"
placeholder="Search..." width={300}
size="sm" placeholder="Search..."
rounded="sm" size="sm"
focusBorderColor="green.500" rounded="sm"
value={searchTerm} focusBorderColor="green.500"
onChange={(e) => setSearchTerm(e.target.value)} value={searchTerm}
/> onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}> <Button
<Pagination totalItems={10} /> onClick={() =>
exportToExcelNew(exportInvestorDetails, "IO Investors Details")
}
leftIcon={<LuFileSpreadsheet />}
colorScheme="forestGreen"
size={"sm"}
variant={"outline"}
rounded={"sm"}
fontSize={"xs"}
w={100}
me={2}
isDisabled={exportInvestorDetails?.length === 0}
>
Export xls
</Button>
<Box as="span">
<Icon
ms={0}
animation={
isRefetchLoading ? `${rotate} 1s linear infinite` : "none"
}
bg={"gray.50"}
onClick={handleRefresh}
fontWeight={600}
as={RepeatIcon}
boxSize={8}
p={2}
rounded={"full"}
_hover={{ bg: "gray.100" }}
cursor={"pointer"}
/>
</Box>
</Box>
<HStack
bg={"#C6F6D5"}
ps={4}
pe={4}
pt={1.5}
pb={1.5}
rounded={"md"}
boxShadow={"sm"}
display={"flex"}
// alignItems={"end"}
// flexDirection={"column"}
alignItems={"center"}
>
<Text
fontWeight={600}
color={"gray.500"}
fontSize={"xs"}
as={"span"}
>
Total Investment Amount ( USD )
</Text>
<Text display={'flex'} alignItems={'center'} fontWeight={600} fontSize={"sm"} as={"span"} pt={"2px"}>
<Badge p={1} ms={2} fontSize={'md'} colorScheme="green" me={0}>
$
</Badge>
{parseFloat(IODetails?.totalAmtInvestmentInUSD).toLocaleString()}
</Text>
</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}

View File

@@ -32,22 +32,22 @@ import {
import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders"; import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
import ToastBox from "../../../Components/ToastBox"; import ToastBox from "../../../Components/ToastBox";
import KeyMeritsEdit from "../KeyMeritsEdit"; import KeyMeritsEdit from "../KeyMeritsEdit";
import SetDisplayOrder from "./SetDisplayOrder"; import SetDisplayOrder from "./SetDisplayOrderKeyMerits";
const KeyMerits = ({ enableNextTab, index, data: prepopData }) => { const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
const toast = useToast(); const toast = useToast();
const params = useParams(); const params = useParams();
// =====================[ variables ] // =====================[ variables ]
const id = params?.id; const id = params?.id;
const { data, isLoading, error } = useGetKeyMeritsQuery(id, { // const { data, isLoading, error } = useGetKeyMeritsQuery(id, {
skip: !id, // skip: !id,
}); // });
console.log(data?.data); const { IODetails} = useContext(GlobalStateContext);
const { keyMerits, setKeyMerits, slideFromRight } =
useContext(GlobalStateContext);
const firstField = useRef(); const firstField = useRef();
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
@@ -57,6 +57,7 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
const [isBtnLoading, setIsBtnLoading] = useState(false); const [isBtnLoading, setIsBtnLoading] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState(""); const [mouseEnteredId, setMouseEnteredId] = useState("");
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const { const {
isOpen: isEditOpen, isOpen: isEditOpen,
onOpen: onEditOpen, onOpen: onEditOpen,
@@ -66,22 +67,11 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
const tableHeadRow = ["Sr.no", "Title", "Sub title", "Icon", "Action"]; const tableHeadRow = ["Sr.no", "Title", "Sub title", "Icon", "Action"];
const handleUpdateStatus = debounce((id) => {
setKeyMerits((prevKeyMerits) =>
prevKeyMerits.map((keyMerits) =>
keyMerits.id === id
? { ...keyMerits, status: !keyMerits.status }
: keyMerits
)
);
toast({
render: () => <ToastBox message={"Status changed succesfully.!"} />,
});
}, 300);
const filteredData = data?.data?.filter((item) => {
const filteredData = IODetails?.keyMerits?.filter((item) => {
// Filter by name (case insensitive) // Filter by name (case insensitive)
const name = item.meritsHeader; const name = item?.meritsHeader;
const searchLower = searchTerm.toLowerCase(); const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower); const nameMatches = name.toLowerCase().includes(searchLower);
@@ -90,7 +80,7 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
// console.log(filteredData); // console.log(filteredData);
const sortedData = filteredData?.sort( const sortedData = filteredData?.sort(
(a, b) => a.displayOder - b.displayOder (a, b) => a.displayOrder - b.displayOrder
); );
const handleDelete = async () => { const handleDelete = async () => {
@@ -107,11 +97,13 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
} catch (error) {} } catch (error) {}
}; };
const extractedArray = sortedData?.map((item, index) => ({ const extractedArray = sortedData?.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={"teal.900"} color={"teal.900"}
fontWeight={"500"} fontWeight={"500"}
@@ -122,7 +114,7 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
), ),
Title: ( Title: (
<Text <Text
justifyContent={slideFromRight ? "right" : "left"} justifyContent={"left"}
as={"span"} as={"span"}
color={"teal.900"} color={"teal.900"}
fontWeight={"500"} fontWeight={"500"}
@@ -139,14 +131,14 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
</Box> </Box>
), ),
Icon: item?.icon?.iconFilePath && ( Icon: item?.icon?.iconFilePath && (
<Image <Image
rounded={"md"} rounded={"md"}
// bg={"#003B14"} // bg={"#003B14"}
display={"flex"} display={"flex"}
p={1} p={1}
justifyContent={"center"} justifyContent={"center"}
alignItems={"center"} alignItems={"center"}
src={" https://tanami.betadelivery.com/" + item?.icon?.iconFilePath} src={import.meta.env.VITE_IMAGE_URL+ item?.icon?.iconFilePath}
w={8} w={8}
h={8} h={8}
/> />
@@ -224,7 +216,7 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
), ),
})); }));
return isLoading ? ( return false ? (
<FullscreenLoaders /> <FullscreenLoaders />
) : ( ) : (
<Box> <Box>
@@ -241,7 +233,9 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
/> */} /> */}
<Box display={"flex"} gap={2} as="span"> <Box display={"flex"} gap={2} as="span">
<SetDisplayOrder data={filteredData} />
{filteredData?.length !== 0 &&<SetDisplayOrder data={filteredData} />}
<Button <Button
leftIcon={<AddIcon />} leftIcon={<AddIcon />}
onClick={onOpen} onClick={onOpen}
@@ -269,7 +263,7 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
isOpen={isEditOpen} isOpen={isEditOpen}
onClose={onEditCloseOpen} onClose={onEditCloseOpen}
firstField={firstField} firstField={firstField}
data={data?.data} data={IODetails?.keyMerits}
/> />
</Box> </Box>
<DataTable <DataTable

View File

@@ -0,0 +1,189 @@
import React, { useState, useEffect } from "react";
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
ModalCloseButton,
useDisclosure,
Button,
Box,
Text,
Image,
HStack,
useToast,
Link,
Badge,
} from "@chakra-ui/react";
import { AddIcon, DragHandleIcon, ExternalLinkIcon } from "@chakra-ui/icons";
import DataTable from "../../../Components/DataTable/DataTable";
import { useSetDisplayOrderIOArtifactsImageMutation } from "../../../Services/io.service";
import ToastBox from "../../../Components/ToastBox";
const SetDisplayOrderIOArtifactsImages = ({ data, }) => {
console.log(data);
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const [isLoading, setIsLoading] = useState(false);
const tableHeadRow = ["", "File Name", "View image"];
const [extractedArray, setExtractedArray] = useState([]);
const [displayOrder, setDisplayOrder] = useState(null);
const [resetDisplayOrder] = useSetDisplayOrderIOArtifactsImageMutation();
// Update state when `data` prop changes
useEffect(() => {
if (data) {
const formattedData = data?.map((item, index) => ({
id: item?.id,
displayOrder: index + 1, // Add displayOrder property
"": (
<Box w={"20px"} isTruncated={true}>
<DragHandleIcon />
</Box>
),
"File Name": (
<Box isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item.artifactName}
</Text>
</Box>
),
"View image": (
<Text
color={"green.500"}
justifyContent={"left"}
as={"span"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
<Badge
px={2}
py={0.5}
display={"flex"}
alignItems={"center"}
textTransform={"inherit"}
fontWeight={500}
colorScheme={"forestGreen"}
>
<Link
href={import.meta.env.VITE_IMAGE_URL + item?.artifactPathName}
isExternal
>
<Box
// onClick={() => {
// setImageSrc(item?.artifactPathName);
// onOpenImageViewer();
// }}
as="span"
cursor={"pointer"}
>
View
</Box>{" "}
<ExternalLinkIcon />
</Link>
</Badge>
</Text>
),
}));
setExtractedArray(formattedData);
}
}, [data]);
// Log the updated order in the desired format whenever `extractedArray` changes
useEffect(() => {
const displayOrderArray = extractedArray.map((item, index) => ({
id: item.id,
displayOrder: index + 1,
}));
setDisplayOrder(displayOrderArray);
}, [extractedArray]);
const handleSetOrder = async () => {
setIsLoading(true);
const data = {
displayOrder: displayOrder,
};
try {
const res = await resetDisplayOrder({ data });
console.log(res?.data?.statusCode);
if (res?.data?.statusCode === 200) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
onClose();
}
setIsLoading(false);
} catch (error) {
console.log(res);
setIsLoading(false);
}
};
return (
<>
<Button
leftIcon={<AddIcon />}
size={"sm"}
fontSize={"xs"}
rounded={"sm"}
variant={"outline"}
colorScheme="forestGreen"
onClick={onOpen}
>
Set Display Order
</Button>
<Modal isCentered size={"xl"} isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader fontSize={"lg"}>Set Display Order</ModalHeader>
<ModalCloseButton />
<ModalBody>
<DataTable
emptyMessage={`We don't have any Sponsors`}
tableHeadRow={tableHeadRow}
data={extractedArray}
setData={setExtractedArray}
isDraggable={true}
/>
</ModalBody>
<ModalFooter>
<Button
size={"sm"}
fontSize={"xs"}
rounded={"sm"}
variant={"outline"}
colorScheme="forestGreen"
mr={3}
onClick={onClose}
>
Reset order
</Button>
<Button
size={"sm"}
fontSize={"xs"}
rounded={"sm"}
colorScheme="forestGreen"
variant="solid"
onClick={handleSetOrder}
isLoading={isLoading}
>
Set order
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default SetDisplayOrderIOArtifactsImages;

View File

@@ -0,0 +1,174 @@
import React, { useState, useEffect } from "react";
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
ModalCloseButton,
useDisclosure,
Button,
Box,
Text,
Image,
HStack,
useToast,
Badge,
Link,
} from "@chakra-ui/react";
import { AddIcon, DragHandleIcon, ExternalLinkIcon } from "@chakra-ui/icons";
import DataTable from "../../../Components/DataTable/DataTable";
import { useSetDisplayOrderIOArtifactsVideoMutation } from "../../../Services/io.service";
import ToastBox from "../../../Components/ToastBox";
const SetDisplayOrderIOArtifactsVideo = ({ data, }) => {
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const [isLoading, setIsLoading] = useState(false);
const tableHeadRow = ["", "File Name", "Video streaming URL"];
const [extractedArray, setExtractedArray] = useState([]);
const [displayOrder, setDisplayOrder] = useState(null);
const [resetDisplayOrder] = useSetDisplayOrderIOArtifactsVideoMutation();
// Update state when `data` prop changes
useEffect(() => {
if (data) {
const formattedData = data.map((item, index) => ({
id: item?.id,
displayOrder: index + 1, // Add displayOrder property
"": (
<Box w={"20px"} isTruncated={true}>
<DragHandleIcon />
</Box>
),
"File Name": (
<Box isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item.artifactName}
</Text>
</Box>
),
"Video streaming URL": (
<Text
color={"green.500"}
justifyContent={"left"}
as={"span"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
<Badge
px={2}
py={0.5}
display={"flex"}
alignItems={"center"}
textTransform={"inherit"}
fontWeight={500}
colorScheme={"forestGreen"}
>
<Link href={item?.artifactStreamingURL} isExternal>
<Box as="span">View</Box> <ExternalLinkIcon />
</Link>
</Badge>
</Text>
),
}));
setExtractedArray(formattedData);
}
}, [data]);
// Log the updated order in the desired format whenever `extractedArray` changes
useEffect(() => {
const displayOrderArray = extractedArray.map((item, index) => ({
id: item.id,
displayOrder: index + 1,
}));
setDisplayOrder(displayOrderArray);
}, [extractedArray]);
const handleSetOrder = async () => {
setIsLoading(true);
const data = {
displayOrder: displayOrder,
};
try {
const res = await resetDisplayOrder({ data });
console.log(res?.data?.statusCode);
if (res?.data?.statusCode === 200) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
onClose();
}
setIsLoading(false);
} catch (error) {
console.log(res);
setIsLoading(false);
}
};
return (
<>
<Button
leftIcon={<AddIcon />}
size={"sm"}
fontSize={"xs"}
rounded={"sm"}
variant={"outline"}
colorScheme="forestGreen"
onClick={onOpen}
>
Set Display Order
</Button>
<Modal isCentered size={"xl"} isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader fontSize={"lg"}>Set Display Order</ModalHeader>
<ModalCloseButton />
<ModalBody>
<DataTable
emptyMessage={`We don't have any Sponsors`}
tableHeadRow={tableHeadRow}
data={extractedArray}
setData={setExtractedArray}
isDraggable={true}
/>
</ModalBody>
<ModalFooter>
<Button
size={"sm"}
fontSize={"xs"}
rounded={"sm"}
variant={"outline"}
colorScheme="forestGreen"
mr={3}
onClick={onClose}
>
Reset order
</Button>
<Button
size={"sm"}
fontSize={"xs"}
rounded={"sm"}
colorScheme="forestGreen"
variant="solid"
onClick={handleSetOrder}
isLoading={isLoading}
>
Set order
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default SetDisplayOrderIOArtifactsVideo;

View File

@@ -0,0 +1,165 @@
import React, { useState, useEffect } from "react";
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
ModalCloseButton,
useDisclosure,
Button,
Box,
Text,
Image,
HStack,
useToast,
} from "@chakra-ui/react";
import { AddIcon, DragHandleIcon } from "@chakra-ui/icons";
import DataTable from "../../../Components/DataTable/DataTable";
import { useSetDisplayOrderIODocumentsMutation } from "../../../Services/io.service";
import ToastBox from "../../../Components/ToastBox";
import { GrDocumentPdf } from "react-icons/gr";
import { TbFileTypeDocx } from "react-icons/tb";
const SetDisplayOrderIODocuments = ({ data, }) => {
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const [isLoading, setIsLoading] = useState(false);
const tableHeadRow = ["", "Type", "File name"];
const [extractedArray, setExtractedArray] = useState([]);
const [displayOrder, setDisplayOrder] = useState(null);
const [resetDisplayOrder] = useSetDisplayOrderIODocumentsMutation();
// Update state when `data` prop changes
useEffect(() => {
if (data) {
const formattedData = data.map((item, index) => ({
id: item?.id,
displayOrder: index + 1, // Add displayOrder property
"": (
<Box maxW={"20px"} isTruncated={true}>
<DragHandleIcon />
</Box>
),
Type: (
<Text
justifyContent={"left"}
as="span"
color="teal.900"
fontWeight="500"
className="d-flex align-items-center"
fontSize={"xl"}
>
{item.documentType === "application/pdf" ? (
<GrDocumentPdf />
) : (
<TbFileTypeDocx fontSize={21} />
)}
</Text>
),
"File name": (
<Text as="span" color="teal.900" fontWeight="500">
{item.documentName}
</Text>
),
}));
setExtractedArray(formattedData);
}
}, [data]);
// Log the updated order in the desired format whenever `extractedArray` changes
useEffect(() => {
const displayOrderArray = extractedArray.map((item, index) => ({
id: item.id,
displayOrder: index + 1,
}));
setDisplayOrder(displayOrderArray);
}, [extractedArray]);
const handleSetOrder = async () => {
setIsLoading(true);
const data = {
displayOrder: displayOrder,
};
try {
const res = await resetDisplayOrder({ data });
console.log(res?.data?.statusCode);
if (res?.data?.statusCode === 200) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
onClose();
}
setIsLoading(false);
} catch (error) {
console.log(res);
setIsLoading(false);
}
};
return (
<>
<Button
leftIcon={<AddIcon />}
size={"sm"}
fontSize={"xs"}
rounded={"sm"}
variant={"outline"}
colorScheme="forestGreen"
onClick={onOpen}
>
Set Display Order
</Button>
<Modal isCentered size={"xl"} isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader fontSize={"lg"}>Set Display Order</ModalHeader>
<ModalCloseButton />
<ModalBody>
<DataTable
emptyMessage={`We don't have any Sponsors`}
tableHeadRow={tableHeadRow}
data={extractedArray}
setData={setExtractedArray}
isDraggable={true}
/>
</ModalBody>
<ModalFooter>
<Button
size={"sm"}
fontSize={"xs"}
rounded={"sm"}
variant={"outline"}
colorScheme="forestGreen"
mr={3}
onClick={onClose}
>
Reset order
</Button>
<Button
size={"sm"}
fontSize={"xs"}
rounded={"sm"}
colorScheme="forestGreen"
variant="solid"
onClick={handleSetOrder}
isLoading={isLoading}
>
Set order
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default SetDisplayOrderIODocuments;

View File

@@ -21,7 +21,7 @@ import DataTable from "../../../Components/DataTable/DataTable";
import { useSetDisplayOrderMutation } from "../../../Services/io.service"; import { useSetDisplayOrderMutation } from "../../../Services/io.service";
import ToastBox from "../../../Components/ToastBox"; import ToastBox from "../../../Components/ToastBox";
const SetDisplayOrder = ({ data }) => { const SetDisplayOrderKeyMerits = ({ data, }) => {
const toast = useToast(); const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
@@ -57,16 +57,20 @@ const SetDisplayOrder = ({ data }) => {
</Box> </Box>
), ),
Icon: item?.icon?.iconFilePath && ( Icon: item?.icon?.iconFilePath && (
<Box
display={"flex"}
justifyContent={"center"}
alignItems={"center"}>
<Image <Image
rounded={"md"} rounded={"md"}
display={"flex"} display={"flex"}
p={1} p={1}
justifyContent={"center"} justifyContent={"center"}
alignItems={"center"} alignItems={"center"}
src={"https://tanami.betadelivery.com/" + item?.icon?.iconFilePath} src={import.meta.env.VITE_IMAGE_URL + item?.icon?.iconFilePath}
w={8} w={8}
h={8} h={8}
/> /></Box>
), ),
})); }));
setExtractedArray(formattedData); setExtractedArray(formattedData);
@@ -164,4 +168,4 @@ const SetDisplayOrder = ({ data }) => {
); );
}; };
export default SetDisplayOrder; export default SetDisplayOrderKeyMerits;

View File

@@ -39,13 +39,8 @@ const InvestmentEdit = ({ isOpen, onClose, thirdField, id, data }) => {
const [updateInvestmentDocuments] = useUpdateInvestmentDocumentsMutation(); const [updateInvestmentDocuments] = useUpdateInvestmentDocumentsMutation();
// =====================[ variables ] // =====================[ variables ]
// const id = params?.id;
console.log(id);
console.log(data);
const filterObject = data?.find((item) => item?.id === id); const filterObject = data?.find((item) => item?.id === id);
console.log(filterObject);
const getFileTitle = (type) => { const getFileTitle = (type) => {
switch (type) { switch (type) {
case "application/pdf": case "application/pdf":
@@ -74,12 +69,16 @@ const InvestmentEdit = ({ isOpen, onClose, thirdField, id, data }) => {
resolver: yupResolver(investmentDocSchema), resolver: yupResolver(investmentDocSchema),
}); });
console.log(errors);
// useEffect to reset the form when `found` changes // useEffect to reset the form when `found` changes
useEffect(() => { useEffect(() => {
if (filterObject) { if (filterObject) {
reset({ reset({
document: filterObject?.documentPath, document: filterObject?.documentPath,
fileName: filterObject?.documentName, fileName: filterObject?.documentName,
documentNameArabic: filterObject?.documentNameArabic,
}); });
} }
}, [filterObject, reset]); }, [filterObject, reset]);
@@ -89,6 +88,7 @@ const InvestmentEdit = ({ isOpen, onClose, thirdField, id, data }) => {
reset({ reset({
fileName: filteredObject?.fileName, fileName: filteredObject?.fileName,
document: filteredObject?.document, document: filteredObject?.document,
documentNameArabic: filterObject?.documentNameArabic,
Type: filteredObject?.Type, Type: filteredObject?.Type,
}); });
} }
@@ -158,6 +158,7 @@ const InvestmentEdit = ({ isOpen, onClose, thirdField, id, data }) => {
if (Object.keys(errors).length === 0) { if (Object.keys(errors).length === 0) {
const formData = new FormData(); const formData = new FormData();
formData.append("documentName", data.fileName); formData.append("documentName", data.fileName);
formData.append("documentNameArabic", data.documentNameArabic);
typeof data?.document !== "string" typeof data?.document !== "string"
? formData.append("document", data?.document[0]) ? formData.append("document", data?.document[0])
: null; : null;
@@ -171,7 +172,6 @@ const InvestmentEdit = ({ isOpen, onClose, thirdField, id, data }) => {
setFile(selectedFile); setFile(selectedFile);
}; };
console.log(filteredObject);
return ( return (
<Drawer <Drawer
@@ -200,6 +200,27 @@ const InvestmentEdit = ({ isOpen, onClose, thirdField, id, data }) => {
</Text> </Text>
)} )}
</FormControl> </FormControl>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">File Name ( Arabic )</FormLabel>
<Input
name="documentNameArabic"
{...register("documentNameArabic")}
fontSize="sm"
type="text"
size="sm"
textAlign={'right'}
/>
{errors.documentNameArabic && (
<Text mt={1} fontSize="xs" fontWeight={500} color="red">
{errors.documentNameArabic.message}
</Text>
)}
</FormControl>
<FormControl mb={4} isInvalid={errors.Type}> <FormControl mb={4} isInvalid={errors.Type}>
<FormLabel fontSize="sm">Document</FormLabel> <FormLabel fontSize="sm">Document</FormLabel>
<Input <Input
@@ -209,6 +230,7 @@ const InvestmentEdit = ({ isOpen, onClose, thirdField, id, data }) => {
className="form-control" className="form-control"
type="file" type="file"
size="sm" size="sm"
accept=".pdf, .doc, .docx"
onChange={handleFileChange} onChange={handleFileChange}
/> />
{errors.document && ( {errors.document && (

View File

@@ -293,7 +293,7 @@ const EditViewIO = () => {
isRequired: true, isRequired: true,
section: " ", section: " ",
width: "32.3%", width: "32.3%",
}, },
{ {
label: "Name (Arabic)", label: "Name (Arabic)",
placeHolder: " ", placeHolder: " ",

View File

@@ -11,13 +11,15 @@ import {
FormControl, FormControl,
FormErrorMessage, FormErrorMessage,
FormLabel, FormLabel,
Icon,
Image, Image,
Input, Input,
Stack, Stack,
Text,
useToast, useToast,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import * as yup from "yup"; import * as yup from "yup";
import React, { useEffect, useState } from "react"; import React, { useEffect, useRef, useState } from "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";
import CustomAlertDialog from "../../Components/CustomAlertDialog"; import CustomAlertDialog from "../../Components/CustomAlertDialog";
@@ -27,9 +29,13 @@ import {
} from "../../Services/io.service"; } from "../../Services/io.service";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import ToastBox from "../../Components/ToastBox"; import ToastBox from "../../Components/ToastBox";
import { bytesToMB } from "./InvestmentDocuments";
import { formatTimestampInGulfTimezone } from "../../Constants/Constants";
import { IoMdRemoveCircleOutline } from "react-icons/io";
const investmentImageSchema = yup.object().shape({ const investmentImageSchema = yup.object().shape({
artifactName: yup.string().required("Artifact image name is required"), artifactName: yup.string().required("Artifact image name is required")
.max(25, "File name must be at most 25 characters"),
artifactPathName: yup.mixed().required("Artifact image is required"), artifactPathName: yup.mixed().required("Artifact image is required"),
}); });
@@ -41,8 +47,6 @@ const IOArtifactsAdd = ({
setActionId, setActionId,
data, data,
}) => { }) => {
console.log(actionId);
console.log(data);
const params = useParams(); const params = useParams();
const id = params?.id; const id = params?.id;
const [file, setFile] = useState(null); const [file, setFile] = useState(null);
@@ -55,7 +59,7 @@ const IOArtifactsAdd = ({
const [updateImageArtifacts] = useUpdateImageArtifactsMutation(); const [updateImageArtifacts] = useUpdateImageArtifactsMutation();
const found = data?.find((item) => item?.id === actionId); const found = data?.find((item) => item?.id === actionId);
console.log(found); const fileInputRef = useRef(null);
const { const {
control, control,
@@ -78,37 +82,48 @@ const IOArtifactsAdd = ({
} }
}, [found, reset]); }, [found, reset]);
console.log(watch());
const onSubmit = async (data) => { const onSubmit = async (data) => {
console.log("hit");
setIsLoading(true); setIsLoading(true);
const formData = new FormData(); const formData = new FormData();
formData.append("artifactName", data.artifactName); formData.append("artifactName", data.artifactName);
file && formData.append("artifactPathName", file); // Assuming artifactPathName is an array of files file && formData.append("artifactPathName", file); // Assuming artifactPathName is an array of files
console.log("FormData:", formData);
for (let [key, value] of formData.entries()) {
console.log(`${key}:`, value);
}
try { try {
if (found) { if (found) {
const res = await updateImageArtifacts({ const res = await updateImageArtifacts({
data: formData, data: formData,
id: found?.id, id: found?.id,
}); });
console.log(res?.error);
if (res?.data?.statusCode === 200) { if (res?.data?.statusCode === 200) {
toast({ toast({
render: () => <ToastBox message={res?.data?.message} />, render: () => <ToastBox message={res?.data?.message} />,
}); });
reset();
setFile(null); setFile(null);
setIsLoading(false); setIsLoading(false);
setAlert(false); setAlert(false);
setPreview(null); setPreview(null);
onClose(); onClose();
reset({
artifactName: "",
artifactPathName: "",
});
} else {
toast({
render: () => (
<ToastBox message={"Something went wronng"} error="error" />
),
});
setFile(null);
setIsLoading(false);
setAlert(false);
setPreview(null);
onClose();
reset({
artifactName: "",
artifactPathName: "",
});
} }
if (res?.error) { if (res?.error) {
@@ -126,7 +141,10 @@ const IOArtifactsAdd = ({
toast({ toast({
render: () => <ToastBox message={res?.data?.message} />, render: () => <ToastBox message={res?.data?.message} />,
}); });
reset(); reset({
artifactName: "",
artifactPathName: "",
});
setFile(null); setFile(null);
setIsLoading(false); setIsLoading(false);
setAlert(false); setAlert(false);
@@ -154,7 +172,11 @@ const IOArtifactsAdd = ({
}; };
const handleSave = () => { const handleSave = () => {
if (!alert) {
setAlert(true)
}else{
handleSubmit(onSubmit)(); handleSubmit(onSubmit)();
}
}; };
const handleClose = () => { const handleClose = () => {
@@ -169,6 +191,20 @@ const IOArtifactsAdd = ({
setActionId(false); setActionId(false);
}; };
const handleRemove = () => {
setFile(null);
setPreview(null);
// Reset the file input value
if (fileInputRef.current) {
fileInputRef.current.value = "";
}
};
const handleAlertClose = () => {
setAlert(false);
setIsLoading(false);
};
return ( return (
<> <>
<Drawer <Drawer
@@ -182,93 +218,141 @@ const IOArtifactsAdd = ({
<DrawerContent> <DrawerContent>
<DrawerCloseButton /> <DrawerCloseButton />
<DrawerHeader fontSize={"sm"}>IO Artifacts Image</DrawerHeader> <DrawerHeader fontSize={"sm"}>IO Artifacts Image</DrawerHeader>
<Box as="form">
<DrawerBody>
<Stack spacing={4}>
<FormControl isInvalid={errors.artifactName} isRequired={true}>
<FormLabel fontSize={"sm"}>Artifact Name</FormLabel>
<Controller
name="artifactName"
control={control}
render={({ field }) => (
<Input
{...field}
fontSize={"sm"}
type="text"
size={"sm"}
maxLength={25}
/>
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.artifactName?.message}
</FormErrorMessage>
</FormControl>
<DrawerBody> <FormControl
<Stack spacing={4}> isInvalid={
<FormControl isInvalid={errors.artifactName}> !preview &&
<FormLabel fontSize={"sm"}>Artifact Name</FormLabel>
<Controller
name="artifactName"
control={control}
render={({ field }) => (
<Input {...field} fontSize={"sm"} type="text" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.artifactName?.message}
</FormErrorMessage>
</FormControl>
<FormControl
isInvalid={
!preview &&
errors.artifactPathName?.message &&
errors.artifactPathName
}
>
<FormLabel fontSize={"sm"}>Artifact Image</FormLabel>
<Input
type="file"
accept="image/*"
onChange={handleFileChange}
fontSize={"sm"}
size={"sm"}
className="form-control"
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{!preview &&
errors.artifactPathName?.message && errors.artifactPathName?.message &&
errors.artifactPathName?.message} errors.artifactPathName
</FormErrorMessage> }
{preview && ( isRequired={true}
<Image >
rounded={"md"} <FormLabel fontSize={"sm"}>Artifact Image (600 X 300)</FormLabel>
src={preview} <Input
alt="Image Preview" type="file"
mt={2} accept=".jpg,.jpeg,.png"
onChange={handleFileChange}
fontSize={"sm"}
size={"sm"}
className="form-control"
ref={fileInputRef} // Set the ref here
/> />
)} <FormErrorMessage fontSize={"xs"} fontWeight={500}>
{found && !preview && ( {!preview &&
<Image errors.artifactPathName?.message &&
rounded={"md"} errors.artifactPathName?.message}
src={ </FormErrorMessage>
"https://tanami.betadelivery.com/" + {preview && (
watch()?.artifactPathName <>
} <Image
alt="Image Preview" rounded={"md"}
mt={2} src={preview}
/> alt="Image Preview"
)} mt={3}
</FormControl> width={300}
</Stack> height={150}
</DrawerBody> objectFit={"cover"}
backgroundSize={"cover"}
/>
<Box
w={"100%"}
position={"relative"}
mt={2}
fontSize={"xs"}
display={"flex"}
flexDirection={"column"}
as="span"
>
<Text as={"span"}>Name: {file?.name}</Text>
<Text as={"span"} fontSize={"xs"}>
File size: {bytesToMB(file?.size)}
</Text>
<Text as={"span"} fontSize={"xs"}>
Last update:{" "}
{formatTimestampInGulfTimezone(file?.lastModified)}
</Text>
<Icon
onClick={() => handleRemove()}
_hover={{ bg: "gray.100" }}
transition={"all 0-5s"}
cursor={"pointer"}
position={"absolute"}
right={0}
p={1}
bottom={0}
rounded={"lg"}
boxSize={7}
as={IoMdRemoveCircleOutline}
/>
</Box>
</>
)}
{found && !preview && (
<Image
rounded={"md"}
src={
import.meta.env.VITE_IMAGE_URL +
watch()?.artifactPathName
}
alt="Image Preview"
mt={2}
/>
)}
</FormControl>
</Stack>
</DrawerBody>
<DrawerFooter> <DrawerFooter mt={5}>
<Button <Button
variant="outline" variant="outline"
colorScheme={"forestGreen"} colorScheme={"forestGreen"}
rounded={"sm"} rounded={"sm"}
size={"sm"} size={"sm"}
mr={3} mr={3}
onClick={onClose} onClick={handleClose}
> >
Cancel Cancel
</Button> </Button>
<Button <Button
colorScheme={"forestGreen"} colorScheme={"forestGreen"}
rounded={"sm"} rounded={"sm"}
size={"sm"} size={"sm"}
onClick={() => setAlert(true)} // onClick={() => setAlert(true)}
> onClick={handleSave}
Save // type="submit"
</Button> >
</DrawerFooter> Save
</Button>
</DrawerFooter>
</Box>
</DrawerContent> </DrawerContent>
</Drawer> </Drawer>
<CustomAlertDialog <CustomAlertDialog
isOpen={alert} isOpen={alert}
onClose={() => setAlert(false)} onClose={handleAlertClose}
alertHandler={handleSave} alertHandler={handleSave}
message={"Are you sure you want to add this artifact?"} message={"Are you sure you want to add this artifact?"}
isLoading={loading} isLoading={loading}

View File

@@ -30,6 +30,7 @@ import CustomAlertDialog from "../../Components/CustomAlertDialog";
const [alert, setAlert] = useState(false); const [alert, setAlert] = useState(false);
const { const {
reset,
control, control,
handleSubmit, handleSubmit,
formState: { errors }, formState: { errors },
@@ -75,7 +76,7 @@ import CustomAlertDialog from "../../Components/CustomAlertDialog";
size={"sm"} size={"sm"}
/> />
</FormControl> </FormControl>
<FormControl mb={4}> <FormControl mb={4}>
<FormLabel fontSize={"sm"}>Vimeo video link *</FormLabel> <FormLabel fontSize={"sm"}>Vimeo video link *</FormLabel>
<Input <Input
value={file} value={file}

View File

@@ -9,6 +9,7 @@ import {
DrawerHeader, DrawerHeader,
DrawerOverlay, DrawerOverlay,
FormControl, FormControl,
FormHelperText,
FormLabel, FormLabel,
Input, Input,
Text, Text,
@@ -58,7 +59,10 @@ export const investmentDocSchema = yup.object().shape({
// console.log("File size:", value ? value.size : "No file"); // console.log("File size:", value ? value.size : "No file");
// return value && value.size <= 2 * 1024 * 1024; // 2MB in bytes // return value && value.size <= 2 * 1024 * 1024; // 2MB in bytes
// }) // })
fileName: yup.string().required("File name is required"), fileName: yup.string().required("File name is required")
.max(25, "File name must be at most 25 characters"), // Maximum length validation,
documentNameArabic: yup.string().required("File name Arabic is required")
.max(25, "File name must be at most 30 characters"),
}); });
const InvestmentDocuments = ({ const InvestmentDocuments = ({
@@ -94,11 +98,10 @@ const InvestmentDocuments = ({
}); });
const onSubmit = async (data) => { const onSubmit = async (data) => {
console.log("sibmited");
console.log(errors);
if (Object.keys(errors).length === 0) { if (Object.keys(errors).length === 0) {
const formData = new FormData(); const formData = new FormData();
formData.append("documentName", data.fileName); formData.append("documentName", data.fileName);
formData.append("documentNameArabic", data.documentNameArabic);
formData.append("document", data?.document[0]); formData.append("document", data?.document[0]);
setFormData(formData); setFormData(formData);
setAlert(true); setAlert(true);
@@ -109,7 +112,7 @@ const InvestmentDocuments = ({
setIsLoading(true); setIsLoading(true);
try { try {
const res = await createInvestmentDocument({ data: formData, id }); const res = await createInvestmentDocument({ data: formData, id });
console.log(res);
if (res?.error) { if (res?.error) {
toast({ toast({
render: () => ( render: () => (
@@ -195,18 +198,19 @@ const InvestmentDocuments = ({
> >
<DrawerOverlay /> <DrawerOverlay />
<DrawerContent> <DrawerContent>
<DrawerCloseButton /> <DrawerCloseButton onClick={handleClose} />
<DrawerHeader fontSize="sm">Add Details</DrawerHeader> <DrawerHeader fontSize="sm">Add Investment Documents</DrawerHeader>
<Box as="form" onSubmit={handleSubmit(onSubmit)}> <Box as="form" onSubmit={handleSubmit(onSubmit)}>
<DrawerBody> <DrawerBody>
<FormControl mb={4} isRequired> <FormControl mb={4} isRequired>
<FormLabel fontSize="sm">File Name</FormLabel> <FormLabel fontSize="sm">File Name</FormLabel>
<Input <Input
name="fileName" name="fileName"
{...register("fileName")} {...register("fileName")}
fontSize="sm" fontSize="sm"
type="text" type="text"
size="sm" size="sm"
maxLength={25} // Maximum length constraint in the input field
/> />
{errors.fileName && ( {errors.fileName && (
<Text mt={1} fontSize="xs" fontWeight={500} color="red"> <Text mt={1} fontSize="xs" fontWeight={500} color="red">
@@ -214,6 +218,28 @@ const InvestmentDocuments = ({
</Text> </Text>
)} )}
</FormControl> </FormControl>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">File Name ( Arabic )</FormLabel>
<Input
name="documentNameArabic"
{...register("documentNameArabic")}
fontSize="sm"
type="text"
size="sm"
textAlign={'right'}
maxLength={30} // Maximum length constraint in the input field
/>
{errors.documentNameArabic && (
<Text mt={1} fontSize="xs" fontWeight={500} color="red">
{errors.documentNameArabic.message}
</Text>
)}
</FormControl>
<FormControl mb={4} isRequired> <FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Document</FormLabel> <FormLabel fontSize="sm">Document</FormLabel>
<Input <Input
@@ -231,6 +257,8 @@ const InvestmentDocuments = ({
{errors.document.message} {errors.document.message}
</Text> </Text>
)} )}
<FormHelperText mt={1} fontSize="xs" fontWeight={500} color="gray.500">File size should be max 2mb</FormHelperText>
</FormControl> </FormControl>
{file && ( {file && (
<Box mt={4}> <Box mt={4}>

View File

@@ -71,7 +71,6 @@ const KeyMeritsAdd = ({ isOpen, onClose, firstField, id, icons }) => {
const onSubmit = (data) => { const onSubmit = (data) => {
if (Object.keys(errors).length === 0) { if (Object.keys(errors).length === 0) {
console.log("hit");
setFormData(data); setFormData(data);
setAlert(true); setAlert(true);
} }
@@ -81,32 +80,26 @@ const KeyMeritsAdd = ({ isOpen, onClose, firstField, id, icons }) => {
setIsLoading(true); setIsLoading(true);
try { try {
const res = await createKeyMerits({ data: formData, id }); const res = await createKeyMerits({ data: formData, id });
console.log(res?.error?.status);
if (res?.data?.statusCode === 201) { if (res?.data?.statusCode === 201) {
toast({ toast({
render: () => <ToastBox message={res?.data?.message} />, render: () => <ToastBox message={res?.data?.message} />,
}); });
setAlert(false); handleClose()
onClose();
setIsLoading(false);
reset();
return; return;
} }
if (res?.error?.data?.code === 400) { if (res?.error?.status === 400 || res?.error?.status === 500 ) {
toast({ toast({
render: () => ( render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} /> <ToastBox message={res?.error?.data?.message} status={"error"} />
), ),
}); });
setIsLoading(false); setIsLoading(false);
onClose();
setAlert(false); setAlert(false);
reset();
setFile(null);
setSelectedImageIcon(null);
setSelectedIcon("Select Icon");
return; return;
} }
} catch (error) { } catch (error) {
if (error) { if (error) {
toast({ toast({
@@ -119,12 +112,7 @@ const KeyMeritsAdd = ({ isOpen, onClose, firstField, id, icons }) => {
}); });
} }
setIsLoading(false); setIsLoading(false);
onClose(); handleClose()
setAlert(false);
reset();
setFile(null);
setSelectedImageIcon(null);
setSelectedIcon("Select Icon");
} }
reset(); reset();
}; };
@@ -135,7 +123,6 @@ const KeyMeritsAdd = ({ isOpen, onClose, firstField, id, icons }) => {
const handleFileChange = (e) => { const handleFileChange = (e) => {
const file = e.target.files[0]; const file = e.target.files[0];
console.log(file);
if (file) { if (file) {
setFile(URL.createObjectURL(file)); setFile(URL.createObjectURL(file));
} }
@@ -149,6 +136,7 @@ const KeyMeritsAdd = ({ isOpen, onClose, firstField, id, icons }) => {
const handleClose = () => { const handleClose = () => {
onClose(); onClose();
setIsLoading(false);
setAlert(false); setAlert(false);
reset(); reset();
setFile(null); setFile(null);
@@ -159,7 +147,7 @@ const KeyMeritsAdd = ({ isOpen, onClose, firstField, id, icons }) => {
return ( return (
<> <>
<Drawer <Drawer
size={"xl"} size={"lg"}
isOpen={isOpen} isOpen={isOpen}
placement="right" placement="right"
initialFocusRef={firstField} initialFocusRef={firstField}
@@ -168,7 +156,7 @@ const KeyMeritsAdd = ({ isOpen, onClose, firstField, id, icons }) => {
<DrawerOverlay /> <DrawerOverlay />
<DrawerContent> <DrawerContent>
<DrawerCloseButton /> <DrawerCloseButton />
<DrawerHeader fontSize={"sm"}>Key Merits </DrawerHeader> <DrawerHeader fontSize={"sm"}>Key Merits</DrawerHeader>
<DrawerBody> <DrawerBody>
<HStack spacing={2} w={"100%"} flexWrap={"wrap"}> <HStack spacing={2} w={"100%"} flexWrap={"wrap"}>
@@ -222,7 +210,7 @@ const KeyMeritsAdd = ({ isOpen, onClose, firstField, id, icons }) => {
isInvalid={!!errors.meritsDescription} isInvalid={!!errors.meritsDescription}
isRequired={true} isRequired={true}
> >
<FormLabel fontSize={"sm"}>Description (English)</FormLabel> <FormLabel fontSize={"sm"}>Sub title</FormLabel>
<Controller <Controller
name="meritsDescription" name="meritsDescription"
control={control} control={control}
@@ -247,7 +235,7 @@ const KeyMeritsAdd = ({ isOpen, onClose, firstField, id, icons }) => {
isInvalid={!!errors.meritsDescriptionArabic} isInvalid={!!errors.meritsDescriptionArabic}
isRequired={true} isRequired={true}
> >
<FormLabel fontSize={"sm"}>Description (Arabic)</FormLabel> <FormLabel fontSize={"sm"}>Sub title (Arabic)</FormLabel>
<Controller <Controller
name="meritsDescriptionArabic" name="meritsDescriptionArabic"
control={control} control={control}
@@ -296,18 +284,18 @@ const KeyMeritsAdd = ({ isOpen, onClose, firstField, id, icons }) => {
} }
> >
<Box display="flex" alignItems="center"> <Box display="flex" alignItems="center">
<Image {selectedImageIcon && <Image
src={`https://tanami.betadelivery.com/${selectedImageIcon}`} src={`${import.meta.env.VITE_IMAGE_URL}${selectedImageIcon}`}
alt={selectedImageIcon} alt={selectedImageIcon}
boxSize="1rem" boxSize="1rem"
mr="12px" mr="12px"
/>{" "} />}{" "}
<Text as={"span"} fontSize={"sm"} fontWeight={500}> <Text as={"span"} fontSize={"sm"} fontWeight={500}>
{selectedIcon} {selectedIcon}
</Text> </Text>
</Box> </Box>
</MenuButton> </MenuButton>
<MenuList minW="415px" size={"sm"} fontWeight={500}> <MenuList overflow={'scroll'} minW="415px" size={"sm"} fontWeight={500}>
{icons?.map(({ iconName, id, iconFilePath }) => ( {icons?.map(({ iconName, id, iconFilePath }) => (
<MenuItem <MenuItem
key={id} key={id}
@@ -317,7 +305,7 @@ const KeyMeritsAdd = ({ isOpen, onClose, firstField, id, icons }) => {
> >
<Box display="flex" alignItems="center"> <Box display="flex" alignItems="center">
<Image <Image
src={`https://tanami.betadelivery.com/${iconFilePath}`} src={`${import.meta.env.VITE_IMAGE_URL}${iconFilePath}`}
alt={iconName} alt={iconName}
boxSize="1rem" boxSize="1rem"
mr="12px" mr="12px"
@@ -351,7 +339,7 @@ const KeyMeritsAdd = ({ isOpen, onClose, firstField, id, icons }) => {
rounded={"sm"} rounded={"sm"}
size={"sm"} size={"sm"}
mr={3} mr={3}
onClick={onClose} onClick={onClose}
> >
Cancel Cancel
</Button> </Button>

View File

@@ -59,6 +59,8 @@ const KeyMeritsEdit = ({
const [selectedImageIcon, setSelectedImageIcon] = useState(null); const [selectedImageIcon, setSelectedImageIcon] = useState(null);
const found = data?.find((item) => item?.id === actionId); const found = data?.find((item) => item?.id === actionId);
console.log(found);
const { const {
control, control,
@@ -71,6 +73,9 @@ const KeyMeritsEdit = ({
}); });
// useEffect to reset the form when `found` changes // useEffect to reset the form when `found` changes
useEffect(() => { useEffect(() => {
setValue("icon_xid", found?.icon?.id);
setSelectedIcon(found?.icon?.iconName); // Update selected icon name
setSelectedImageIcon(found?.icon?.iconFilePath);
if (found) { if (found) {
reset({ reset({
meritsHeader: found?.meritsHeader, meritsHeader: found?.meritsHeader,
@@ -91,10 +96,7 @@ const KeyMeritsEdit = ({
toast({ toast({
render: () => <ToastBox message={res?.data?.message} />, render: () => <ToastBox message={res?.data?.message} />,
}); });
setAlert(false); handleClose()
onClose();
setIsLoading(false);
reset();
return; return;
} }
if (res?.error?.data?.code === 400) { if (res?.error?.data?.code === 400) {
@@ -103,10 +105,7 @@ const KeyMeritsEdit = ({
<ToastBox message={res?.error?.data?.message} status={"error"} /> <ToastBox message={res?.error?.data?.message} status={"error"} />
), ),
}); });
setAlert(false); handleClose()
onClose();
setIsLoading(false);
reset();
return; return;
} }
} catch (error) { } catch (error) {
@@ -121,10 +120,7 @@ const KeyMeritsEdit = ({
), ),
}); });
} }
setIsLoading(false); handleClose()
setAlert(false);
onClose();
reset();
} }
reset(); reset();
}; };
@@ -133,7 +129,6 @@ const KeyMeritsEdit = ({
handleSubmit(onSubmit)(); handleSubmit(onSubmit)();
}; };
console.log(errors);
const handleIconSelect = (id, iconName, iconFilePath) => { const handleIconSelect = (id, iconName, iconFilePath) => {
setValue("icon_xid", id); setValue("icon_xid", id);
@@ -141,6 +136,14 @@ const KeyMeritsEdit = ({
setSelectedImageIcon(iconFilePath); setSelectedImageIcon(iconFilePath);
}; };
const handleClose = () => {
setIsLoading(false);
setAlert(false);
onClose();
reset();
}
return ( return (
<> <>
<Drawer <Drawer
@@ -280,12 +283,12 @@ const KeyMeritsEdit = ({
} }
> >
<Box display="flex" alignItems="center"> <Box display="flex" alignItems="center">
<Image {selectedImageIcon&&<Image
src={`https://tanami.betadelivery.com/${selectedImageIcon}`} src={`${import.meta.env.VITE_IMAGE_URL}${selectedImageIcon}`}
alt={selectedImageIcon} alt={selectedImageIcon}
boxSize="1rem" boxSize="1rem"
mr="12px" mr="12px"
/>{" "} />}{" "}
<Text as={"span"} fontSize={"sm"} fontWeight={500}> <Text as={"span"} fontSize={"sm"} fontWeight={500}>
{selectedIcon} {selectedIcon}
</Text> </Text>
@@ -301,7 +304,7 @@ const KeyMeritsEdit = ({
> >
<Box display="flex" alignItems="center"> <Box display="flex" alignItems="center">
<Image <Image
src={`https://tanami.betadelivery.com/${iconFilePath}`} src={`${import.meta.env.VITE_IMAGE_URL}${iconFilePath}`}
alt={iconName} alt={iconName}
boxSize="1rem" boxSize="1rem"
mr="12px" mr="12px"

View File

@@ -1,3 +1,4 @@
import React, { useContext, useEffect, useState } from "react";
import { import {
Box, Box,
Button, Button,
@@ -12,84 +13,245 @@ import {
ModalHeader, ModalHeader,
ModalOverlay, ModalOverlay,
Text, Text,
Textarea, useToast,
} from "@chakra-ui/react"; } 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";
// 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 AmountInvested = ({ isOpen, onClose }) => { const AmountInvested = ({ isOpen, onClose }) => {
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();
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 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,
});
};
return ( return (
<Modal isOpen={isOpen} onClose={onClose}> <Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay /> <ModalOverlay />
<ModalContent> <ModalContent>
<ModalHeader fontSize={'md'}>Amount Invested</ModalHeader> <ModalHeader fontSize={"md"}>Amount Invested</ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody> <ModalBody>
<FormControl mb={"15px"}> <form onSubmit={handleSubmit(onSubmit)}>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}> <FormControl
Date mb={"15px"}
</FormLabel> isInvalid={!!errors.transactionDate}
<Input isRequired
placeholder="Select Date" >
size="sm" <FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
rounded={'sm'} Date
fontSize={"sm"} </FormLabel>
focusBorderColor="forestGreen.300" <Input
type="date" type="date"
/> {...register("transactionDate")}
</FormControl> size="sm"
rounded={"sm"}
fontSize={"sm"}
focusBorderColor="forestGreen.300"
/>
{errors.transactionDate && (
<Text fontSize={"xs"} fontWeight={600} color="red.500">
{errors.transactionDate.message}
</Text>
)}
</FormControl>
<FormControl mb={"15px"} > <FormControl
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>Amount</FormLabel> mb={"15px"}
<Input isInvalid={!!errors.Total_Amount}
size="sm" isReadOnly
rounded={'sm'} >
textAlign={'end'} <FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
readOnly Amount
value={"$ 100000"} </FormLabel>
focusBorderColor="forestGreen.300" <Input
fontSize={"sm"} placeholder="$00.00" /> type="text"
</FormControl> value={formatCurrency(watch("Total_Amount"))}
size="sm"
rounded={"sm"}
textAlign={"end"}
focusBorderColor="forestGreen.300"
fontSize={"sm"}
readOnly
/>
{errors.Total_Amount && (
<Text fontSize={"xs"} fontWeight={600} color="red.500">
{errors.Total_Amount.message}
</Text>
)}
</FormControl>
<FormControl mb={"15px"} > <FormControl
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>Amount to invest</FormLabel> mb={"15px"}
<Input isInvalid={!!errors.amountInvested}
size="sm" isRequired
rounded={'sm'} >
textAlign={'end'} <FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
focusBorderColor="forestGreen.300" Amount to invest
fontSize={"sm"} placeholder="$00.00" /> </FormLabel>
</FormControl> {/* <Input
type="number"
{...register('amountInvested')}
size="sm"
rounded={'sm'}
textAlign={'end'}
focusBorderColor="forestGreen.300"
fontSize={"sm"}
onChange={handleAmountChange}
/> */}
<Controller
name="amountInvested"
control={control}
render={({ field }) => (
<CurrencyInput
{...field}
textAlign={"right"}
fontSize={"sm"}
type="number"
size={"sm"}
onChange={(value) => {
field.onChange(value); // This will keep the form's internal state updated
handleAmountChange(value); // This will trigger your custom logic
}}
/>
)}
/>
{errors.amountInvested && (
<Text fontSize={"xs"} fontWeight={600} color="red.500">
{errors.amountInvested.message}
</Text>
)}
</FormControl>
<FormControl mb={"15px"}> <FormControl mb={"15px"} isInvalid={!!errors.IoCash}>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}> <FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
IO Cash IO Cash
</FormLabel> </FormLabel>
<Input <Input
size="sm" type="text"
rounded={'sm'} value={formatCurrency(watch("IoCash"))}
placeholder="$00.00" size="sm"
focusBorderColor="forestGreen.300" rounded={"sm"}
fontSize={"sm"} /> focusBorderColor="forestGreen.300"
</FormControl> fontSize={"sm"}
textAlign={"right"}
readOnly
/>
{errors.IoCash && (
<Text fontSize={"xs"} fontWeight={600} color="red.500">
{errors.IoCash.message}
</Text>
)}
</FormControl>
<ModalFooter>
<Button
type="submit"
bg={"hsla(139, 100%, 14%, 1)"}
mr={3}
color={"#fff"}
_hover={{
bg: "hsl(139deg 98.99% 26.59%)",
}}
size={"sm"}
rounded={"sm"}
isLoading={isLoading}
>
Save
</Button>
<Button size={"sm"} rounded={"sm"} mr={3} onClick={onClose}>
Close
</Button>
</ModalFooter>
</form>
</ModalBody> </ModalBody>
<ModalFooter>
<Button
bg={"hsla(139, 100%, 14%, 1)"}
mr={3}
color={"#fff"}
_hover={{
bg: "hsl(139deg 98.99% 26.59%)",
}}
size={'sm'}
rounded={"sm"}
>
Save
</Button>
<Button
size={'sm'}
rounded={"sm"} mr={3} onClick={onClose}>
Close
</Button>
</ModalFooter>
</ModalContent> </ModalContent>
</Modal> </Modal>
); );

View File

@@ -1,7 +1,10 @@
import { import {
Alert,
AlertIcon,
Box, Box,
Button, Button,
FormControl, FormControl,
FormErrorMessage,
FormLabel, FormLabel,
HStack, HStack,
Input, Input,
@@ -19,12 +22,87 @@ import {
Textarea, Textarea,
Th, Th,
Tr, Tr,
useToast,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import DataTable from "../../../../Components/DataTable/DataTable"; import NormalData from "../../../../Components/DataTable/NormalTable";
import { useState } from "react"; import { useContext, useState } from "react";
import { AddIcon } from "@chakra-ui/icons"; import { AddIcon } from "@chakra-ui/icons";
import {
useGetDistributedToInvestorMutation,
useGetDistributionInvestorMutation,
useUpdateExitToInvestorMutation,
} from "../../../../Services/io.service";
import { useParams } from "react-router-dom";
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import ToastBox from "../../../../Components/ToastBox";
import CurrencyInput from "../../../../Components/CurrencyInput";
import { IoCalculator } from "react-icons/io5";
import { debounce } from "../../../Master/Sponser/AddSponser";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
const DistributionInvestor = ({ isOpen, onClose }) => { const DistributionInvestor = ({ isOpen, onClose }) => {
const params = useParams();
const toast = useToast();
const id = params?.id;
const [ isCalculateLoading, setIsCalculateLoading ] = useState(false)
const [ isFinalCalculateLoading, setIsFinalCalculateLoading ] = useState(false)
const [ calcualtedData, setCalculatedDate ] = useState(null)
const [ isCalcualtedData, setIsCalcualtedData ] = useState(false)
const { IODetails } = useContext(GlobalStateContext);
// const {
// data:IObyID,
// error,
// isLoading,
// } = useGetDistributionInvestorMutation(id);
const [getDistributionInvestment] = useGetDistributionInvestorMutation();
const [getFinalDistributionInvestment] =
useGetDistributedToInvestorMutation();
const [updateExitToInvestor] = useUpdateExitToInvestorMutation();
const investorExit = yup.object().shape({
amount: yup
.string()
.required("Amount is required")
.test(
"max",
`Distribution amount should not be greater than IO cash amount ${IODetails?.ioCash}`,
function(value) {
const { ioCash } = IODetails || {}; // Safely get ioCash
if (value && ioCash) {
return parseFloat(value) <= parseFloat(ioCash); // Ensure both are compared as numbers
}
return true; // If ioCash is not available, skip validation
}
),
});
const investor = yup.object().shape({
amount: yup.string().required("Amount is required"),
});
const {
control,
handleSubmit,
formState: { errors },
reset,
} = useForm({
resolver: yupResolver(investorExit),
});
// ====================================================[Table Setup]================================================================ // ====================================================[Table Setup]================================================================
const tableHeadRow = [ const tableHeadRow = [
"Sr No.", "Sr No.",
@@ -32,96 +110,94 @@ const DistributionInvestor = ({ isOpen, onClose }) => {
"First name", "First name",
"Last Name", "Last Name",
"Amount", "Amount",
"%", "Holding (%)",
"($)", "Distriution Amt($)",
"Yeild (%)",
]; ];
const filteredData = [ const extractedArray = calcualtedData?.data?.map((item, index) => ({
{ id: item?.id,
id: 1, "Sr No.": (
}, <Box
{ w={9}
id: 1, display={"flex"}
}, alignItems={"center"}
{ isTruncated={true}
id: 1, h={25}
}, >
{ <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
id: 1, {index + 1}
}, </Text>
]; </Box>
),
const [extractedArray, setExtractedArray] = useState( "Client Id": (
filteredData?.map((item, index) => ({ <Box w={100} isTruncated={true}>
id: item?.id, <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
"Sr No.": ( {item?.clientId}
<Box </Text>
w={9} </Box>
display={"flex"} ),
alignItems={"center"} "First name": (
isTruncated={true} <Box minW={24} isTruncated={true}>
h={25} <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
> {item?.firstName}
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> </Text>
{index + 1} </Box>
</Text> ),
</Box> "Last Name": (
), <Box minW={24} isTruncated={true}>
"Client Id": ( <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
<Box w={100} isTruncated={true}> {item?.lastName}
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> </Text>
BH00000001 </Box>
</Text> ),
</Box> Amount: (
), <Box minW={24} isTruncated={true}>
"First name": ( <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
<Box minW={24} isTruncated={true}> {item?.amount?.toLocaleString(undefined, {
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> minimumFractionDigits: 2,
Faisal maximumFractionDigits: 2,
</Text> })}
</Box> </Text>
), </Box>
"Last Name": ( ),
<Box minW={24} isTruncated={true}> "Holding (%)": (
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> <Box minW={24} isTruncated={true}>
Aljalahma <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
</Text> {item?.investor_holidings?.toLocaleString(undefined, {
</Box> minimumFractionDigits: 2,
), maximumFractionDigits: 2,
Amount: ( })}%
<Box minW={24} isTruncated={true}> </Text>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> </Box>
$100,000 /- ),
</Text> "Distriution Amt($)": (
</Box> <Box minW={24} isTruncated={true}>
), <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
"%": ( {item?.distribution_amt?.toLocaleString(undefined, {
<Box minW={19} isTruncated={true}> minimumFractionDigits: 2,
<Text maximumFractionDigits: 2,
textAlign={"right"} })}
as={"span"} </Text>
color={"teal.900"} </Box>
fontWeight={"500"} ),
> "Yeild (%)": (
26.0 % <Box minW={24} isTruncated={true}>
</Text> <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
</Box> {item?.distribution_per?.toLocaleString(undefined, {
), minimumFractionDigits: 2,
"($)": ( maximumFractionDigits: 2,
<Box minW={24} isTruncated={true}> })}%
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> </Text>
$100,000 /- </Box>
</Text> ),
</Box> }));
),
}))
);
const Total = () => { const Total = () => {
return ( return (
<Table size="sm"> <Table size="sm">
<Tbody backgroundColor="gray.50"> <Tbody>
<Tr> <Tr backgroundColor="gray.50">
<Th <Th
textAlign={"left"} textAlign={"left"}
p={3} p={3}
@@ -175,7 +251,10 @@ const DistributionInvestor = ({ isOpen, onClose }) => {
wordBreak="normal" wordBreak="normal"
overflowWrap="normal" overflowWrap="normal"
> >
$100200 {calcualtedData?.totalInvestedAmt?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Th> </Th>
<Th <Th
textAlign={"left"} textAlign={"left"}
@@ -185,8 +264,9 @@ const DistributionInvestor = ({ isOpen, onClose }) => {
whiteSpace="normal" whiteSpace="normal"
wordBreak="normal" wordBreak="normal"
overflowWrap="normal" overflowWrap="normal"
opacity={0}
> >
100.0% {calcualtedData?.distributed_per?.toFixed(2)}%
</Th> </Th>
<Th <Th
textAlign={"center"} textAlign={"center"}
@@ -197,7 +277,10 @@ const DistributionInvestor = ({ isOpen, onClose }) => {
wordBreak="normal" wordBreak="normal"
overflowWrap="normal" overflowWrap="normal"
> >
$100230 {calcualtedData?.distributed_amt?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Th> </Th>
</Tr> </Tr>
</Tbody> </Tbody>
@@ -205,54 +288,179 @@ const DistributionInvestor = ({ isOpen, onClose }) => {
); );
}; };
const onSubmit = async (data) => {
setIsCalculateLoading(true);
try {
const res = await getDistributionInvestment({ id, data });
console.log(res?.data?.data);
if (res?.error?.status === 401) {
toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} />
),
});
setIsCalculateLoading(false);
setIsCalcualtedData(false);
} else if (res?.data?.statusCode === 200) {
setCalculatedDate(res?.data?.data);
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
setIsCalculateLoading(false);
setIsCalcualtedData(true);
}
} catch (error) {}
};
const onFinalSubmit = async (data) => {
setIsFinalCalculateLoading(true);
if (!isCalcualtedData) {
setIsFinalCalculateLoading(false);
return toast({
render: () => (
<ToastBox
message={"Please calculate investment first."}
status="warn"
/>
),
});
}
const finalData = {
transactionAmount: data?.amount,
};
try {
const res = await getFinalDistributionInvestment({ id, data: finalData });
console.log(finalData);
if (res?.error?.status === 401) {
toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status="error" />
),
});
} else if (res?.data?.statusCode === 200) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
handleClose();
}
} catch (error) {
console.error("An error occurred:", error);
} finally {
handleClose();
}
};
const handleClose = () => {
onClose();
setIsFinalCalculateLoading(false);
reset({
amount:""
});
setCalculatedDate(null);
setIsCalcualtedData(false);
};
return ( return (
<Modal size={"xl"} isOpen={isOpen} onClose={onClose}> <Modal size={"xl"} isOpen={isOpen} onClose={handleClose}>
<ModalOverlay /> <ModalOverlay />
<ModalContent maxW={1000}> <ModalContent maxW={1200}>
<ModalHeader fontSize={"md"}>Distribution To Investor Transaction</ModalHeader> <ModalHeader fontSize={"md"}>
Distribution To Investor Transaction
</ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody> <ModalBody>
<Text as="label" mb='5px' fontSize='sm' fontWeight={500}>Amount to Distribute</Text> {/* <Text as="label" mb="5px" fontSize="sm" fontWeight={500}>
<HStack mb={4}> Amount to Distribute
<Input placeholder="$00.00" size={"sm"} className="col" /> </Text> */}
<HStack onSubmit={handleSubmit(onSubmit)} as={"form"} mb={4}>
{/* <Input placeholder="$00.00" size={"sm"} className="col" /> */} {/* <Input placeholder="$00.00" size={"sm"} className="col" /> */}
<Button <FormControl isInvalid={errors.amount} isRequired>
// leftIcon={<AddIcon />} <FormLabel textAlign={"right"} fontSize={"sm"}>
size={"sm"} {" "}
rounded={"sm"} Amount to Distribute
colorScheme="forestGreen" </FormLabel>
> <Box
Calculate display={"flex"}
</Button> justifyContent={"end"}
alignItems={"end"}
gap={2}
>
<Controller
name="amount"
control={control}
render={({ field }) => (
<CurrencyInput
rounded={0}
w={"18%"}
{...field}
fontSize={"sm"}
type="number"
size={"sm"}
textAlign={"right"}
/>
)}
/>
<Button
leftIcon={<IoCalculator />}
size={"sm"}
rounded={0}
colorScheme="forestGreen"
type="submit"
isLoading={isCalculateLoading}
>
Calculate
</Button>
</Box>
<FormErrorMessage display={'flex'} justifyContent={'end'} pe={125} fontSize={"xs"} fontWeight={600}>
{errors.amount?.message}
</FormErrorMessage>
</FormControl>
</HStack> </HStack>
<DataTable {calcualtedData ? (
emptyMessage={`We don't have any Sponers `} <NormalData
tableHeadRow={tableHeadRow} emptyMessage={`We don't have any Sponers `}
data={extractedArray} tableHeadRow={tableHeadRow}
setData={setExtractedArray} data={extractedArray}
caption={<Total />} // total={<Total />}
// isLoading={isLoading} // isLoading={isLoading}
/> />
) : (
<Alert status="info" fontSize={"sm"} rounded={"sm"}>
<AlertIcon />
Please enter amount to calcutale Distribution!
</Alert>
)}
</ModalBody> </ModalBody>
<ModalFooter pt={0}> <ModalFooter pt={0}>
<Button {isCalcualtedData ? (
bg={"hsla(139, 100%, 14%, 1)"} <>
mr={3} <Button
color={"#fff"} bg={"hsla(139, 100%, 14%, 1)"}
_hover={{ mr={3}
bg: "hsl(139deg 98.99% 26.59%)", color={"#fff"}
}} _hover={{
size={'sm'} bg: "hsl(139deg 98.99% 26.59%)",
rounded={"sm"} }}
> size={"sm"}
Save rounded={"sm"}
</Button> onClick={handleSubmit(onFinalSubmit)}
<Button isLoading={isFinalCalculateLoading}
size={'sm'} >
rounded={"sm"} mr={3} onClick={onClose}> Save
Close </Button>
</Button> <Button size={"sm"} rounded={"sm"} mr={3} onClick={onClose}>
Close
</Button>
</>
) : (
""
)}
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>
</Modal> </Modal>

View File

@@ -1,7 +1,10 @@
import { import {
Alert,
AlertIcon,
Box, Box,
Button, Button,
FormControl, FormControl,
FormErrorMessage,
FormLabel, FormLabel,
HStack, HStack,
Input, Input,
@@ -19,115 +22,208 @@ import {
Textarea, Textarea,
Th, Th,
Tr, Tr,
useToast,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import DataTable from "../../../../Components/DataTable/DataTable"; import NormalData from "../../../../Components/DataTable/NormalTable";
import { useState } from "react"; import { useContext, useState } from "react";
import { AddIcon } from "@chakra-ui/icons"; import { AddIcon } from "@chakra-ui/icons";
import {
useGetDistributedToInvestorMutation,
useGetDistributionInvestorMutation,
useUpdateExitToInvestorMutation,
} from "../../../../Services/io.service";
import { useParams } from "react-router-dom";
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import ToastBox from "../../../../Components/ToastBox";
import CurrencyInput from "../../../../Components/CurrencyInput";
import { IoCalculator } from "react-icons/io5";
import { debounce } from "../../../Master/Sponser/AddSponser";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
const Exit = ({ isOpen, onClose }) => { const Exit = ({ isOpen, onClose }) => {
const params = useParams();
const toast = useToast();
const id = params?.id;
const [isCalculateLoading, setIsCalculateLoading] = useState(false);
const [isFinalCalculateLoading, setIsFinalCalculateLoading] = useState(false);
const [calcualtedData, setCalculatedDate] = useState(null);
const [isCalcualtedData, setIsCalcualtedData] = useState(false);
const { IODetails } = useContext(GlobalStateContext);
const investorExit = yup.object().shape({
amount: yup
.string()
.required("Amount is required")
.test(
"max",
`Distribution amount should not be greater than IO cash amount ${IODetails?.ioCash}`,
function (value) {
const { ioCash } = IODetails || {}; // Safely get ioCash
if (value && ioCash) {
return parseFloat(value) <= parseFloat(ioCash); // Ensure both are compared as numbers
}
return true; // If ioCash is not available, skip validation
}
),
});
const {
control,
handleSubmit,
formState: { errors },
reset,
} = useForm({
resolver: yupResolver(investorExit),
});
useEffect(() => {
console.log("hiit useEffectc");
handleCalculate(id, {
amount: IODetails?.ioMVNAV,
});
reset({
amount: IODetails?.ioMVNAV,
});
}, [IODetails, id]);
const handleCalculate = async (id, data) => {
try {
const res = await getDistributionInvestment({ id, data });
console.log(res?.data?.data);
if (res?.error?.status === 401) {
// toast({
// render: () => (
// <ToastBox message={res?.error?.data?.message} status={"error"} />
// ),
// });
setIsCalculateLoading(false);
setIsCalcualtedData(false);
} else if (res?.data?.statusCode === 200) {
setCalculatedDate(res?.data?.data);
// toast({
// render: () => <ToastBox message={res?.data?.message} />,
// });
setIsCalculateLoading(false);
setIsCalcualtedData(true);
}
} catch (error) {}
};
// const {
// data:IObyID,
// error,
// isLoading,
// } = useGetDistributionInvestorMutation(id);
const [getDistributionInvestment] = useGetDistributionInvestorMutation();
const [getFinalDistributionInvestment] =
useGetDistributedToInvestorMutation();
const [updateExitToInvestor] = useUpdateExitToInvestorMutation();
const investor = yup.object().shape({
amount: yup.string().required("Amount is required"),
});
// useEffect(()=>{
// try {
// const res = getDistributionInvestment({id,data})
// console.log(res);
// } catch (error) {
// }
// },[])
// console.log(IObyID);
// ====================================================[Table Setup]================================================================ // ====================================================[Table Setup]================================================================
const tableHeadRow = [ const tableHeadRow = [
"Sr No.", "Sr No.",
"Client Id", "Client Id",
"First name", "First name",
"Last Name", "Last Name",
"Invested Amount", "Amount",
"Distribution %", "Holding (%)",
"Exit Amount", "Exit Amt($)",
]; ];
const filteredData = [ const extractedArray = calcualtedData?.data?.map((item, index) => ({
{ id: item?.id,
id: 1, "Sr No.": (
}, <Box
{ w={9}
id: 1, display={"flex"}
}, alignItems={"center"}
{ isTruncated={true}
id: 1, h={25}
}, >
{ <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
id: 1, {index + 1}
}, </Text>
{ </Box>
id: 1, ),
}, "Client Id": (
{ <Box w={100} isTruncated={true}>
id: 1, <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
}, {item?.clientId}
]; </Text>
</Box>
const [extractedArray, setExtractedArray] = useState( ),
filteredData?.map((item, index) => ({ "First name": (
id: item?.id, <Box minW={24} isTruncated={true}>
"Sr No.": ( <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
<Box {item?.firstName}
w={9} </Text>
display={"flex"} </Box>
alignItems={"center"} ),
isTruncated={true} "Last Name": (
h={25} <Box minW={24} isTruncated={true}>
> <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> {item?.lastName}
{index + 1} </Text>
</Text> </Box>
</Box> ),
), Amount: (
"Client Id": ( <Box minW={24} isTruncated={true}>
<Box w={100} isTruncated={true}> <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> {item?.amount?.toLocaleString(undefined, {
BH0000000 minimumFractionDigits: 2,
</Text> maximumFractionDigits: 2,
</Box> })}
), </Text>
"First name": ( </Box>
<Box minW={24} isTruncated={true}> ),
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> "Holding (%)": (
Faisal <Box minW={24} isTruncated={true}>
</Text> <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
</Box> {item?.investor_holidings?.toLocaleString(undefined, {
), minimumFractionDigits: 2,
"Last Name": ( maximumFractionDigits: 2,
<Box minW={24} isTruncated={true}> })}%
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> </Text>
Aljalahma </Box>
</Text> ),
</Box> "Exit Amt($)": (
), <Box minW={24} isTruncated={true}>
"Invested Amount": ( <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
<Box minW={24} isTruncated={true}> {item?.distribution_amt?.toLocaleString(undefined, {
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> minimumFractionDigits: 2,
$100,000 maximumFractionDigits: 2,
</Text> })}
</Box> </Text>
), </Box>
"Distribution %": ( ),
<Box minW={19} isTruncated={true}> }));
<Text
textAlign={"right"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
>
26.0 %
</Text>
</Box>
),
"Exit Amount": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
$100,000
</Text>
</Box>
),
}))
);
const Total = () => { const Total = () => {
return ( return (
<Table size="sm"> <Table size="sm">
<Tbody backgroundColor="gray.50"> <Tbody>
<Tr> <Tr backgroundColor="gray.50">
<Th <Th
textAlign={"left"} textAlign={"left"}
p={3} p={3}
@@ -142,7 +238,7 @@ const Exit = ({ isOpen, onClose }) => {
<Th <Th
textAlign={"center"} textAlign={"center"}
p={3} p={3}
width="100px" width="110px"
color={"#004118"} color={"#004118"}
whiteSpace="normal" whiteSpace="normal"
wordBreak="normal" wordBreak="normal"
@@ -153,7 +249,7 @@ const Exit = ({ isOpen, onClose }) => {
<Th <Th
textAlign={"center"} textAlign={"center"}
p={3} p={3}
width="90px" width="110px"
color={"#004118"} color={"#004118"}
whiteSpace="normal" whiteSpace="normal"
wordBreak="normal" wordBreak="normal"
@@ -164,7 +260,7 @@ const Exit = ({ isOpen, onClose }) => {
<Th <Th
textAlign={"center"} textAlign={"center"}
p={3} p={3}
width="90px" width="110px"
color={"#004118"} color={"#004118"}
whiteSpace="normal" whiteSpace="normal"
wordBreak="normal" wordBreak="normal"
@@ -175,13 +271,16 @@ const Exit = ({ isOpen, onClose }) => {
<Th <Th
textAlign={"left"} textAlign={"left"}
p={3} p={3}
width="100px" width="110px"
color={"#004118"} color={"#004118"}
whiteSpace="normal" whiteSpace="normal"
wordBreak="normal" wordBreak="normal"
overflowWrap="normal" overflowWrap="normal"
> >
$1,000,000 {calcualtedData?.totalInvestedAmt?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Th> </Th>
<Th <Th
textAlign={"left"} textAlign={"left"}
@@ -191,8 +290,9 @@ const Exit = ({ isOpen, onClose }) => {
whiteSpace="normal" whiteSpace="normal"
wordBreak="normal" wordBreak="normal"
overflowWrap="normal" overflowWrap="normal"
opacity={0}
> >
100.0% {calcualtedData?.distributed_per?.toFixed(2)}%
</Th> </Th>
<Th <Th
textAlign={"center"} textAlign={"center"}
@@ -203,7 +303,10 @@ const Exit = ({ isOpen, onClose }) => {
wordBreak="normal" wordBreak="normal"
overflowWrap="normal" overflowWrap="normal"
> >
$1,229,750 {calcualtedData?.distributed_amt?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Th> </Th>
</Tr> </Tr>
</Tbody> </Tbody>
@@ -211,52 +314,150 @@ const Exit = ({ isOpen, onClose }) => {
); );
}; };
const onSubmit = async (data) => {
setIsCalculateLoading(true);
try {
const res = await getDistributionInvestment({ id, data });
console.log(res?.data?.data);
if (res?.error?.status === 401) {
toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} />
),
});
setIsCalculateLoading(false);
setIsCalcualtedData(false);
} else if (res?.data?.statusCode === 200) {
setCalculatedDate(res?.data?.data);
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
setIsCalculateLoading(false);
setIsCalcualtedData(true);
}
} catch (error) {}
};
const onFinalSubmit = async (data) => {
console.log("hit");
setIsFinalCalculateLoading(true);
// if (!isCalcualtedData) {
// setIsFinalCalculateLoading(false);
// return toast({
// render: () => (
// <ToastBox
// message={"Please calculate investment first."}
// status="warn"
// />
// ),
// });
// }
const finalData = {
transactionAmount: IODetails?.ioMVNAV,
};
try {
const res = await updateExitToInvestor({ id, data: finalData });
console.log(finalData);
if (res?.error?.status === 401) {
toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status="error" />
),
});
} else if (res?.data?.statusCode === 200) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
handleClose();
}
} catch (error) {
console.error("An error occurred:", error);
} finally {
handleClose();
}
};
const handleClose = () => {
onClose();
setIsFinalCalculateLoading(false);
setIsCalcualtedData(false);
};
return ( return (
<Modal size={"xl"} isOpen={isOpen} onClose={onClose}> <Modal size={"xl"} isOpen={isOpen} onClose={handleClose}>
<ModalOverlay /> <ModalOverlay />
<ModalContent maxW={1000}> <ModalContent maxW={1000}>
<ModalHeader fontSize={"md"}>Exit Transaction</ModalHeader> <ModalHeader fontSize={"md"}>Exit Transaction</ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody> <ModalBody>
<Text as="label" mb='5px' fontSize='sm' fontWeight={500}>Enter Exit Amount</Text> {/* <Text as="label" mb="5px" fontSize="sm" fontWeight={500}>
<HStack mb={4}> Amount to Distribute
<Input focusBorderColor="forestGreen.400" placeholder="$00.00" size={"sm"} className="col" /> </Text> */}
<Button <HStack onSubmit={handleSubmit(onSubmit)} as={"form"} mb={4} alignItems={'center'}>
size={"sm"} {/* <Input placeholder="$00.00" size={"sm"} className="col" /> */}
rounded={"sm"} {/* <FormControl isInvalid={errors.amount} isRequired>*/}
colorScheme="forestGreen" <Text textAlign={"right"} fontSize={"sm"}>
Exit Amount :
</Text>
<Text
textAlign={"start"}
bg={"green.100"}
p={2}
rounded={"md"}
fontSize={"sm"}
pt={1}
pb={1}
fontWeight={600}
> >
Calculate ${" "}
</Button> {parseFloat(IODetails?.ioMVNAV || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
{/* </FormControl> */}
</HStack> </HStack>
<DataTable {/* {calcualtedData && ( */}
<NormalData
emptyMessage={`We don't have any Sponers `} emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow} tableHeadRow={tableHeadRow}
data={extractedArray} data={extractedArray}
setData={setExtractedArray} // total={<Total />}
caption={<Total />}
// isLoading={isLoading} // isLoading={isLoading}
/> />
{/* ) } */}
</ModalBody> </ModalBody>
<ModalFooter pt={0}> <ModalFooter pt={0}>
<Button {isCalcualtedData ? (
bg={"hsla(139, 100%, 14%, 1)"} <>
mr={3} <Button
color={"#fff"} bg={"hsla(139, 100%, 14%, 1)"}
_hover={{ mr={3}
bg: "hsl(139deg 98.99% 26.59%)", color={"#fff"}
}} _hover={{
size={'sm'} bg: "hsl(139deg 98.99% 26.59%)",
rounded={"sm"} }}
> size={"sm"}
Save rounded={"sm"}
</Button> onClick={() => onFinalSubmit()}
<Button isLoading={isFinalCalculateLoading}
size={'sm'} >
rounded={"sm"} mr={3} onClick={onClose}> Save
Close </Button>
</Button> <Button size={"sm"} rounded={"sm"} mr={3} onClick={onClose}>
Close
</Button>
</>
) : (
""
)}
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>
</Modal> </Modal>

View File

@@ -2,6 +2,7 @@ import {
Box, Box,
Button, Button,
FormControl, FormControl,
FormErrorMessage,
FormLabel, FormLabel,
Input, Input,
Modal, Modal,
@@ -11,52 +12,168 @@ import {
ModalFooter, ModalFooter,
ModalHeader, ModalHeader,
ModalOverlay, ModalOverlay,
Select,
Stack,
Text, Text,
Textarea, Textarea,
useToast,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { useContext, useState } from "react";
import * as yup from "yup";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import { Controller, useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import CurrencyInput from "../../../../Components/CurrencyInput";
import { useCreateIoNavMutation } from "../../../../Services/io.service";
import ToastBox from "../../../../Components/ToastBox";
import { useParams } from "react-router-dom";
import { formatDatee } from "../../../../Components/FormField";
const ioNav = yup.object().shape({
transactionDate: yup.string().required("Artifact name is required"),
// ioTransType_xid: yup.number().required("Artifact name is required"),
transactionAmount: yup.number().required("Artifact name is required"),
comments: yup.string().notRequired(),
});
const UpdateIONav = ({ isOpen, onClose }) => { const UpdateIONav = ({ isOpen, onClose }) => {
const params = useParams()
const toast = useToast();
const id = params?.id
const { IODetails } = useContext(GlobalStateContext);
const [isLoading, setIsLoading] = useState(false)
const {
control,
handleSubmit,
watch,
reset,
formState: { errors },
} = useForm({
resolver: yupResolver(ioNav),
});
const [createIoNav] = useCreateIoNavMutation()
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) {
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.error?.data?.message} status={"error"} />,
});
}
} catch (error) {
console.log(error);
}
}
const handleClose = () => {
onClose()
reset()
}
const today = formatDatee(new Date(), 'yyyy-MM-dd');
return ( return (
<Modal isOpen={isOpen} onClose={onClose}> <Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay /> <ModalOverlay />
<ModalContent> <ModalContent as={'form'} onSubmit={handleSubmit(onSubmit)} >
<ModalHeader fontSize={'md'}>Update iO NAV Transaction</ModalHeader> <ModalHeader fontSize={'md'}>Update iO NAV Transaction</ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody> <ModalBody>
<FormControl mb={"15px"}>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
Date
</FormLabel>
<Input
placeholder="Select Date"
size="sm"
rounded={'sm'}
fontSize={"sm"}
focusBorderColor="forestGreen.300"
type="date"
/>
</FormControl>
<FormControl mb={"15px"} > <Stack spacing={4}>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>Amount</FormLabel> <FormControl isInvalid={errors.transactionDate} isRequired>
<Input <FormLabel fontSize={"sm"}>Date Selection</FormLabel>
size="sm" <Controller
rounded={'sm'} name="transactionDate"
textAlign={'end'} control={control}
focusBorderColor="forestGreen.300" render={({ field }) => (
fontSize={"sm"} placeholder="$00.00" /> <Input {...field}
</FormControl> 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 mb={"15px"}>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}> {/* <FormControl isInvalid={errors.ioTransType_xid} isRequired>
Comments <FormLabel fontSize={"sm"}>Cash transaction</FormLabel>
</FormLabel> <Controller
<Textarea name="ioTransType_xid"
size="sm" control={control}
rounded={'sm'} render={({ field }) => (
focusBorderColor="forestGreen.300" <Select
fontSize={"sm"} placeholder="Write Comments" /> {...field}
</FormControl> placeholder="Select an option"
fontSize={"sm"}
size={"sm"}
>
{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={'right'} fontSize={"sm"} type="text" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.comments?.message}
</FormErrorMessage>
</FormControl>
</Stack>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button <Button
@@ -68,10 +185,12 @@ const UpdateIONav = ({ isOpen, onClose }) => {
}} }}
size={'sm'} size={'sm'}
rounded={"sm"} rounded={"sm"}
type="submit"
isLoading={isLoading}
> >
Save Save
</Button> </Button>
<Button <Button
size={'sm'} size={'sm'}
rounded={"sm"} mr={3} onClick={onClose}> rounded={"sm"} mr={3} onClick={onClose}>
Close Close

View File

@@ -1,8 +1,9 @@
import { ChevronDownIcon } from "@chakra-ui/icons"; import { ChevronDownIcon } from "@chakra-ui/icons";
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { import {
Badge, Badge,
Button, Button,
FormControl,
FormLabel, FormLabel,
Menu, Menu,
MenuButton, MenuButton,
@@ -15,25 +16,31 @@ import {
ModalFooter, ModalFooter,
ModalHeader, ModalHeader,
ModalOverlay, ModalOverlay,
FormErrorMessage,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { import {
useGetIOprepopulateDataQuery, useGetIOprepopulateDataQuery,
useUpdateCancleStatusMutation,
useUpdateCancleStatusToMutation,
useUpdateStatusIoMutation, useUpdateStatusIoMutation,
} from "../../../../Services/io.service"; } from "../../../../Services/io.service";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
const UpdateIOStatus = ({ isOpen, onClose }) => { const UpdateIOStatus = ({ isOpen, onClose, status }) => {
const params = useParams(); const params = useParams();
const id = params?.id; const id = params?.id;
const [selectedItem, setSelectedItem] = useState("Open"); const [selectedItem, setSelectedItem] = useState();
const [isLoadingg, setIsLoading] = useState(false); const [isLoadingg, setIsLoading] = useState(false);
const { data, error, isLoading } = useGetIOprepopulateDataQuery(); const [error, setError] = useState("");
const [selectedStatusId, setSelectedStatusId] = useState(data?.data?.ioStatus[0]?.id); const [selectedStatusId, setSelectedStatusId] = useState("");
const { data } = useGetIOprepopulateDataQuery();
const [updateStatusIo] = useUpdateStatusIoMutation(); const [updateStatusIo] = useUpdateStatusIoMutation();
console.log(data?.data?.ioStatus); const [updateCancleStatus] = useUpdateCancleStatusToMutation();
console.log(data?.data?.ioStatus); // useEffect(() => {
// setSelectedStatusId(status?.[0]?.id);
// }, [status]);
const handleMenuItemClick = (item, id) => { const handleMenuItemClick = (item, id) => {
setSelectedItem(item); setSelectedItem(item);
@@ -41,64 +48,155 @@ const UpdateIOStatus = ({ isOpen, onClose }) => {
}; };
const handleSubmit = async () => { const handleSubmit = async () => {
setIsLoading(true) if (!selectedStatusId) {
setError("Please select status");
return;
}
setError("");
setIsLoading(true);
try { try {
const res = await updateStatusIo({ let res;
data: {
ioStatus_xid: selectedStatusId, // If selectedItem is 'Cancelled', make the updateCancelStatus API call
}, if (selectedItem === "Cancelled") {
id, res = await updateCancleStatus({
}); id
console.log(res); });
setIsLoading(false) }
onClose() // Otherwise, make the updateStatusIo API call
} catch (error) {} else {
res = await updateStatusIo({
data: {
ioStatus_xid: selectedStatusId,
},
id,
});
}
console.log("API Response:", res);
setIsLoading(false);
handleClose();
} catch (error) {
setIsLoading(false);
}
};
const handleClose = () => {
setSelectedItem(null);
setSelectedStatusId(null);
onClose();
setError("");
}; };
return ( return (
<Modal isOpen={isOpen} onClose={onClose}> <Modal isOpen={isOpen} onClose={handleClose}>
<ModalOverlay /> <ModalOverlay />
<ModalContent> <ModalContent>
<ModalHeader fontSize={"md"}>Update IO Status Transaction</ModalHeader> <ModalHeader fontSize={"md"}>Update IO Status Transaction</ModalHeader>
<ModalCloseButton /> <ModalCloseButton />
<ModalBody> <ModalBody>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}> <FormControl isInvalid={!!error}>
Status <FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
</FormLabel> Status
<Menu> </FormLabel>
<MenuButton <Menu>
as={Button} <MenuButton
rightIcon={<ChevronDownIcon />} as={Button}
fontSize={"sm"} rightIcon={<ChevronDownIcon />}
fontWeight={500} fontSize={"sm"}
w={"100%"} fontWeight={500}
textAlign={"left"} w={"100%"}
> textAlign={"left"}
{selectedItem} >
</MenuButton> {selectedItem ? (
<MenuList w={"400px"}> <Badge
{data?.data?.ioStatus?.map(({ id, statusAdmin }) => ( rounded={"full"}
<MenuItem pt={1.5}
key={id} pb={1.5}
fontSize={"sm"} ps={4}
onClick={() => handleMenuItemClick(statusAdmin, id)} pe={4}
> mt={1.5}
<Badge py={"1px"} px={"8px"}> mb={1.5}
{statusAdmin} textTransform={"none"}
colorScheme={
selectedItem === "Draft"
? "gray"
: selectedItem === "Processing"
? "yellow"
: selectedItem === "Open"
? "blue"
: selectedItem === "Closed"
? "green"
: selectedItem === "Exited"
? "red"
: selectedItem === "Cancelled"
? "orange"
: "purple"
}
py={"3px"}
px={"8px"}
>
{selectedItem}
</Badge> </Badge>
</MenuItem> ) : (
))} "Select Item"
</MenuList> )}
</Menu> </MenuButton>
{status?.length > 0 ? (
<MenuList w={"400px"}>
{status?.map(({ id, statusAdmin }) => (
<MenuItem
key={id}
fontSize={"sm"}
onClick={() => handleMenuItemClick(statusAdmin, id)}
>
<Badge
rounded={"full"}
pt={1.5}
pb={1.5}
ps={4}
pe={4}
mt={1.5}
mb={1.5}
textTransform={"none"}
colorScheme={
statusAdmin === "Draft"
? "gray"
: statusAdmin === "Processing"
? "yellow"
: statusAdmin === "Open"
? "blue"
: statusAdmin === "Closed"
? "green"
: statusAdmin === "Exited"
? "red"
: statusAdmin === "Cancelled"
? "orange"
: "purple"
}
py={"1px"}
px={"8px"}
>
{statusAdmin}
</Badge>
</MenuItem>
))}
</MenuList>
) : (
""
)}
</Menu>
<FormErrorMessage fontSize={"xs"} fontWeight={600}>
{error}
</FormErrorMessage>
</FormControl>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button <Button
bg={"hsla(139, 100%, 14%, 1)"} colorScheme="forestGreen"
mr={3} mr={3}
color={"#fff"} color={"#fff"}
_hover={{
bg: "hsl(139deg 98.99% 26.59%)",
}}
size={"sm"} size={"sm"}
rounded={"sm"} rounded={"sm"}
onClick={handleSubmit} onClick={handleSubmit}
@@ -106,7 +204,7 @@ const UpdateIOStatus = ({ isOpen, onClose }) => {
> >
Save Save
</Button> </Button>
<Button size={"sm"} rounded={"sm"} mr={3} onClick={onClose}> <Button size={"sm"} rounded={"sm"} mr={3} onClick={handleClose}>
Close Close
</Button> </Button>
</ModalFooter> </ModalFooter>

View File

@@ -15,6 +15,7 @@ import {
Tag, Tag,
Text, Text,
Tooltip, 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";
@@ -37,44 +38,79 @@ import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import ToastBox from "../../../Components/ToastBox"; import ToastBox from "../../../Components/ToastBox";
import { useGetIOsQuery } from "../../../Services/io.service"; import { useGetIOsQuery } from "../../../Services/io.service";
import { TABLE_PAGINATION } from "../../../Constants/Paginations"; import { TABLE_PAGINATION } from "../../../Constants/Paginations";
import { formatCurrency } from "../../../Components/CurrencyInput";
import { IoIosPhonePortrait } from "react-icons/io";
import MobileView from "../../../Components/MobileView";
import { ImMobile } from "react-icons/im";
import {
generateSerialNumber,
removeTrailingZeros,
} from "../../../Constants/Constants";
// import { debounce } from "./AddIOCharges"; // import { debounce } from "./AddIOCharges";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
}; // Simple date formatter
const ViewIOTable = () => { const ViewIOTable = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const toast = useToast(); const toast = useToast();
const { IODetails, setIODetails, slideFromRight } = const { IODetails, setIODetails, slideFromRight } =
useContext(GlobalStateContext); useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [statusFilter, setStatusFilter] = useState("all");
// 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("");
// ===============================[ Paginations ] const { isOpen: isOpen, onOpen: onOpen, onClose: onClose } = useDisclosure();
// ===============================[ Paginations ]
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size); const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page); const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const [searchTerm, setSearchTerm] = useState("");
const [statusFilter, setStatusFilter] = 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]);
// ===============================[ RTK Api calls ] =============================================
const { data, isLoading, error } = useGetIOsQuery({
page: currentPage,
size: pageSize,
ioStatus_xid: statusFilter,
search: debouncedSearchTerm,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
});
// ===============================[ RTK Api calls ]
const {
data,
isLoading,
error,
} = useGetIOsQuery({ page: currentPage, size: pageSize });
console.log(data); console.log(data);
// ===============================[ Table Header ] // ===============================[ Table Header ]
const tableHeadRow = [ const tableHeadRow = [
"Sr No.",
"IO ID", "IO ID",
"IO Name", "IO Name",
"Sponsorer", "Sponsor",
"Investment Type", "Investment Type",
"Goal Amount", "Goal Amount",
"Holding Period", "Amount Raised",
"Closing Date",
"IO Status", "IO Status",
// "Preview",
"Action", "Action",
]; ];
@@ -94,9 +130,19 @@ const ViewIOTable = () => {
return nameMatches && statusMatches; return nameMatches && statusMatches;
}); });
console.log(filteredData); const extractedArray = data?.data?.rows?.map((item, idx) => ({
"Sr No.": (
const extractedArray = filteredData?.map((item, index) => ({ <Text
w={"24px"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center fw-bold web-text-small"
>
{/* {item.id} */}
{generateSerialNumber(idx, currentPage, pageSize)}
</Text>
),
"IO ID": ( "IO ID": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
@@ -105,13 +151,31 @@ const ViewIOTable = () => {
</Box> </Box>
), ),
"IO Name": ( "IO Name": (
<Box w={"auto"} isTruncated={true}> <Tooltip
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> hasArrow
bg="#c6f6d5"
fontSize={"xs"}
label={item.investmentNameEnglish ? item.investmentNameEnglish : "---"}
placement="top-start"
color={"blue.800"}
>
<Text
as={"span"}
color={"teal.900"}
fontWeight={"500"}
maxWidth="100px" // Adjust width as needed
display="block" // Ensure block display for proper truncation
overflow="hidden"
isTruncated
textOverflow="ellipsis"
cursor={"pointer"}
>
{item.investmentNameEnglish ? item.investmentNameEnglish : "---"} {item.investmentNameEnglish ? item.investmentNameEnglish : "---"}
</Text> </Text>
</Box> </Tooltip>
), ),
Sponsorer: ( Sponsor: (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.sponsor?.sponsorName ? item.sponsor?.sponsorName : "---"} {item?.sponsor?.sponsorName ? item.sponsor?.sponsorName : "---"}
@@ -119,86 +183,139 @@ const ViewIOTable = () => {
</Box> </Box>
), ),
"Investment Type": ( "Investment Type": (
<Box w={"auto"} isTruncated={true}> <Box w={"120px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.investmentType?.investmentTypeName ? item.investmentType?.investmentTypeName : "---"} {item?.investmentType?.investmentTypeName
? item.investmentType?.investmentTypeName
: "---"}
</Text> </Text>
</Box> </Box>
), ),
"Goal Amount": ( "Goal Amount": (
<Box w={"auto"} isTruncated={true}> <Box w={"100%"} display={"flex"} justifyContent={"start"} alignItems={'center'}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item.goalAmount ? item.goalAmount : "---"} <Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{`${parseFloat(item.goalAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text> </Text>
</Box> </Box>
), ),
"Holding Period": ( "Amount Raised": (
<Box w={"100%"} display={"flex"} justifyContent={"start"} alignItems={'center'}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{/* {item.goalAmount
? formatCurrency(removeTrailingZeros(item.goalAmount))
: "---"} */}
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{`${parseFloat(item.totalRaisedAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text>
</Box>
),
"Closing Date": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}> <Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item.holdingPeriod ? item.holdingPeriod : "---"} {/* {item.closingDate ? item.closingDate : "---"} */}
{formatDate(item.closingDate ? item.closingDate : "---")}
</Text> </Text>
</Box> </Box>
), ),
"IO Status": ( "IO Status": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Badge <Badge
rounded={"sm"} rounded={"full"}
pt={0.5} pt={1}
pb={0.5} pb={1}
ps={4} ps={4}
pe={4} pe={4}
mt={1.5}
mb={1.5}
textTransform={"none"} textTransform={"none"}
color={ // variant={"solid"}
item?.ioStatus?.statusAdmin === "Draft"
? "yellow.500"
: item.ioStatus === "Pending"
? "#6226EF"
: "#EF3826"
}
colorScheme={ colorScheme={
item?.ioStatus?.statusAdmin === "Draft" item?.ioStatus?.statusAdmin === "Draft"
? "gray"
: item?.ioStatus?.statusAdmin === "Processing"
? "yellow" ? "yellow"
: item.ioStatus?.statusAdmin === "Pending" : item?.ioStatus?.statusAdmin === "Open"
? "purple" ? "blue"
: "red" : item?.ioStatus?.statusAdmin === "Closed"
? "green"
: item?.ioStatus?.statusAdmin === "Exited"
? "red"
: item?.ioStatus?.statusAdmin === "Canclled"
? "orange"
: "purple"
} }
boxShadow={"0 4px 6px rgba(0, 0, 0, 0.1)"} // Adjusted shadow
> >
{item.ioStatus?.statusAdmin} {item.ioStatus?.statusAdmin}
</Badge> </Badge>
</Box> </Box>
), ),
Preview: (
<Box display={"flex"} justifyContent={"start"}>
<Badge
display={"flex"}
px={2}
py={1}
alignItems={"center"}
color={"#000"}
fontWeight={500}
bg="purple.200"
onClick={() => {
setActionId(item.id);
onOpen();
}}
rounded={"sm"}
size={"xs"}
variant={"ghost"}
cursor={"pointer"}
>
<ImMobile className="me-1" /> View
</Badge>
</Box>
),
Action: ( Action: (
<Box display={"flex"} justifyContent={"center"} gap={2}> <Box display={"flex"} justifyContent={"center"} gap={2}>
<Tooltip {/* <Tooltip
rounded={"sm"} rounded={"sm"}
fontSize={"xs"} fontSize={"xs"}
label="View" label="View"
bg="#fff" bg="#fff"
color={"green.500"} color={"green.500"}
placement="top" placement="top"
> */}
<Button
// _hover={{ color: "green.500" }}
colorScheme="green"
// transition={"0.5s all"}
onClick={() => {
navigate(`/view-io/${item.id}`);
}}
// color="green.300"
rounded={"sm"}
size={"xs"}
> >
<Button <ViewIcon me={2} /> View
// _hover={{ color: "green.500" }} </Button>
colorScheme="green" {/* </Tooltip> */}
// transition={"0.5s all"}
onClick={() => {
navigate(`/view-io/${item.id}`);
}}
// color="green.300"
rounded={"sm"}
size={"xs"}
>
<ViewIcon />
</Button>
</Tooltip>
<Tooltip {/* <Tooltip
rounded={"sm"} rounded={"sm"}
fontSize={"xs"} fontSize={"xs"}
label="View" label="Edit"
bg="#fff" bg="#fff"
color={"green.500"} color={"blue.500"}
placement="top" placement="top"
> >
<Button <Button
@@ -214,7 +331,7 @@ const ViewIOTable = () => {
> >
<EditIcon /> <EditIcon />
</Button> </Button>
</Tooltip> </Tooltip> */}
{/* <Tooltip {/* <Tooltip
rounded={"sm"} rounded={"sm"}
@@ -244,8 +361,6 @@ const ViewIOTable = () => {
const handleDelete = () => {}; const handleDelete = () => {};
console.log(extractedArray);
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">
@@ -278,22 +393,27 @@ const ViewIOTable = () => {
cursor={"pointer"} cursor={"pointer"}
value={statusFilter} // Use the value prop here value={statusFilter} // Use the value prop here
> >
<option value="all" selected disabled hidden defaultChecked> <option value="" selected disabled hidden defaultChecked>
Status Status
</option> </option>
<option value="all">All</option> <option value="">All</option>
<option value="Draft">Draft</option> <option value="1">Draft</option>
<option value="Cancelled">Cancelled</option> <option value="6">Cancelled</option>
<option value="Processing">Processing</option> <option value="3">Processing</option>
<option value="Open">Open</option> <option value="2">Open</option>
<option value="Exited">Exited</option> <option value="5">Exited</option>
<option value="Closed">Closed</option> <option value="4">Closed</option>
</Select> </Select>
<Pagination isLoading={isLoading} pageSize={pageSize} setPageSize={setPageSize} currentPage={currentPage} setCurrentPage={setCurrentPage} totalItems={data?.data?.totalItems} />
<Pagination
isLoading={isLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={data?.data?.totalItems}
/>
{/* <Link to={"/create-io"}> {/* <Link to={"/create-io"}>
<Button <Button
@@ -325,6 +445,8 @@ const ViewIOTable = () => {
setMouseEntered={setMouseEntered} setMouseEntered={setMouseEntered}
/> />
{/* <MobileView isOpen={isOpen} onClose={onClose} actionId={actionId} /> */}
<CustomAlertDialog <CustomAlertDialog
onClose={() => setDeleteAlert(false)} onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert} isOpen={deleteAlert}

View File

@@ -29,36 +29,52 @@ import ViewIOnav from "./ViewIOnav";
import ViewDistribution from "./ViewDistribution"; import ViewDistribution from "./ViewDistribution";
import InvestmentDocument from "../CreateIO/InvestmentDocument"; import InvestmentDocument from "../CreateIO/InvestmentDocument";
import KeyMerits from "../CreateIO/KeyMerits"; import KeyMerits from "../CreateIO/KeyMerits";
import Investors from "../CreateIO/Investors" import Investors from "../CreateIO/Investors";
import EditIO from "../EditIO/EditIO"; import EditIO from "../EditIO/EditIO";
import IOArtifacts from "../CreateIO/IOArtifacts"; import IOArtifacts from "../CreateIO/IOArtifacts";
import IOCashDetails from "../CreateIO/IOCashDetails"; import IOCashDetails from "../CreateIO/IOCashDetails";
import IONAVDetails from "../CreateIO/IONAVDetails"; import IONAVDetails from "../CreateIO/IONAVDetails";
import { useGetIOprepopulateDataQuery } from "../../../Services/io.service"; import { useGetIOprepopulateDataQuery } from "../../../Services/io.service";
import UnderConstruction from "../../UnderConstruction";
import Destribution from "../CreateIO/Destribution";
const ViewIOdata = () => { const ViewIOdata = () => {
const params = useParams() const params = useParams();
const id = params?.id const id = params?.id;
const { data, error, isLoading } = useGetIOprepopulateDataQuery(); const { data, error, isLoading } = useGetIOprepopulateDataQuery();
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const navigate = useNavigate(); const navigate = useNavigate();
const [isEditing, setIsEditing] = useState(false); const [isEditing, setIsEditing] = useState(false);
const { IODetails, setIODetails } = useContext(GlobalStateContext);
console.log(IODetails?.isInvestedAmount);
const tabs = [ const tabs = [
{ label: "IO Details", content: <ViewIOdetails data={data?.data} /> }, { label: "IO Details", content: <ViewIOdetails data={data?.data} /> },
{ label: "Investment documents", content: <InvestmentDocument data={data?.data} /> }, {
label: "Investment documents",
content: <InvestmentDocument data={data?.data} />,
},
{ label: "Key merits", content: <KeyMerits data={data?.data} /> }, { label: "Key merits", content: <KeyMerits data={data?.data} /> },
{ label: "IO artifacts", content: <IOArtifacts data={data?.data} /> }, { label: "IO artifacts", content: <IOArtifacts data={data?.data} /> },
{ label: "Investors", content: <Investors data={data?.data} /> }, { label: "Investors", content: <Investors data={data?.data} /> },
// { label: "Investors", content: <UnderConstruction h={'75vh'} /> },
{ label: "IO Cash Details", content: <IOCashDetails data={data?.data} /> }, { label: "IO Cash Details", content: <IOCashDetails data={data?.data} /> },
{ label: "IO NAV Details", content: <IONAVDetails data={data?.data} /> }, { label: "IO NAV Details", content: <IONAVDetails data={data?.data} /> },
// { label: "Distribution", content: <ViewDistribution /> }, {
label: "Distribution to Investors",
content: <Destribution data={data?.data} />,
},
// { label: "Distribution to Investors", content: <UnderConstruction h={'75vh'} /> },
]; ];
return ( return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} overflowX={"hidden"} height={"100vh"} pb={14} > <Box
{...OPACITY_ON_LOAD}
overflowY={"scroll"}
overflowX={"hidden"}
height={"100vh"}
pb={14}
>
<Box paddingInline={"12px"} mt={2}> <Box paddingInline={"12px"} mt={2}>
{/* <span {/* <span
onClick={() => navigate(-1)} onClick={() => navigate(-1)}
@@ -70,21 +86,30 @@ const ViewIOdata = () => {
</Box> </Box>
<Tabs mt={4}> <Tabs mt={4}>
<TabList justifyContent={'space-between'} pe={4} alignItems={'center'}> <TabList justifyContent={"space-between"} pe={4} alignItems={"center"}>
<Box display={'flex'} > <Box display={"flex"}>
{tabs.map(({ label }, index) => ( {tabs.map(({ label }, index) => (
<Tab <Tab
disabled={true} px={3}
key={index} isDisabled={
fontSize={"sm"} index === 0 ||
_selected={{ index === 1 ||
color: "#004118", index === 2 ||
borderBottom: "2px solid #38a169", index === 3 ||
}} index === 4
> ? false
{label} : !IODetails?.isInvestedAmount
</Tab> }
))} key={index}
fontSize={"sm"}
_selected={{
color: "#004118",
borderBottom: "2px solid #38a169",
}}
>
{label}
</Tab>
))}
</Box> </Box>
</TabList> </TabList>
<TabPanels> <TabPanels>

View File

@@ -23,6 +23,7 @@ import {
Badge, Badge,
Box, Box,
Icon, Icon,
HStack,
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import header from "../../../assets/IOheader.png"; import header from "../../../assets/IOheader.png";
import { HiDotsVertical } from "react-icons/hi"; import { HiDotsVertical } from "react-icons/hi";
@@ -39,17 +40,16 @@ import Exit from "./HeaderModal/Exit";
import Cancle from "./HeaderModal/Cancle"; import Cancle from "./HeaderModal/Cancle";
import { AddIcon } from "@chakra-ui/icons"; import { AddIcon } from "@chakra-ui/icons";
import { GrGallery } from "react-icons/gr"; import { GrGallery } from "react-icons/gr";
import Loader01 from "../../../Components/Loaders/Loader01";
// import { formatCurrency } from "../../../Components/CurrencyInput";
// import { removeTrailingZeros } from "../../../Constants/Constants";
const ViewIOdataHeader = () => { const ViewIOdataHeader = ({ data, isLoading }) => {
const params = useParams(); const params = useParams();
const id = params?.id; const id = params?.id;
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const btnRef = useRef(); const btnRef = useRef();
const { IODetails } = useContext(GlobalStateContext); const { IODetails, isIOloading } = useContext(GlobalStateContext);
console.log(
"=================>>>>>",
IODetails?.artifactsImage?.[0]?.artifactPathName
);
const { const {
isOpen: isInvestmentOpen, isOpen: isInvestmentOpen,
@@ -91,27 +91,103 @@ const ViewIOdataHeader = () => {
onOpen: onCancleOpen, onOpen: onCancleOpen,
onClose: onCancleClose, onClose: onCancleClose,
} = useDisclosure(); } = useDisclosure();
const bg = { const bg = {
bg: "#fff", bg: "#fff",
}; };
const hover = { const hover = {
textDecoration: "underline", textDecoration: "underline",
background: "#fff", background: "#fff",
}; };
const style = { const style = {
fontSize: "0.875rem", fontSize: "0.875rem",
fontWeight: "400", fontWeight: "400",
}; };
return (
console.log(
import.meta.env.VITE_IMAGE_URL +
IODetails?.artifactsImage?.[0]?.artifactPathName
);
const menu = [
{
id: 1,
title: "Amount Invested",
onClickFunction: onInvestmentOpen,
},
// {
// id:2,
// title:"Fees & Expenses",
// onClickFunction:onFeesOpen
// },
// {
// id:3,
// title:"Distribution From Sponsors",
// onClickFunction:onDistSponsorOpen
// },
{
id: 6,
title: "Distribution To Investors",
onClickFunction: onDistInvestorOpen,
},
{
id: 5,
title: "Update IO NAV",
onClickFunction: onUpdateNavOpen,
},
{
id: 8,
title: "Exit",
onClickFunction: onExitOpen,
},
{
id: 9,
title: "Cancel",
onClickFunction: onCancleOpen,
},
{
id: 10,
title: "Update IO Status",
onClickFunction: onUpdateStatusOpen,
},
];
// console.log(IODetails?.mainTranscation);
// Extract titles from apiTransaction
const apiTransactionTitles = IODetails?.mainTranscation?.map(
(transaction) => transaction.id
);
// Filter menu items
const filteredMenu = menu?.filter((item) =>
apiTransactionTitles?.includes(item.id)
);
const balanceAmount = IODetails?.goalAmount - IODetails?.totalAmtInvestmentInUSD
return IODetails?.investmentNameEnglish ? (
<Box <Box
display={"flex"} display={"flex"}
alignItems={"center"} alignItems={"center"}
justifyContent={"start"} justifyContent={"space-between"}
gap={8} gap={8}
bg={"#caf5d8"} bg={
IODetails?.ioStatus?.statusAdmin === "Draft"
? "#EDF2F7"
: IODetails?.ioStatus?.statusAdmin === "Processing"
? "#FEFBBF"
: IODetails?.ioStatus?.statusAdmin === "Open"
? "#BEE2F8"
: IODetails?.ioStatus?.statusAdmin === "Closed"
? "#C6F6D5"
: IODetails?.ioStatus?.statusAdmin === "Exited"
? "#FED7D7"
: IODetails?.ioStatus?.statusAdmin === "Cancelled"
? "#E9D8FD"
: IODetails?.ioStatus?.statusAdmin === "DeActivate"
? "#E9D8FD"
: null
}
rounded={"md"} rounded={"md"}
// bgGradient='linear(to-r, #caf5d8, #f5e8ca)' // bgGradient='linear(to-r, #caf5d8, #f5e8ca)'
// bgGradient='linear(to-r, #caf5d8, #d4a5a5)' // bgGradient='linear(to-r, #caf5d8, #d4a5a5)'
@@ -120,127 +196,244 @@ const ViewIOdataHeader = () => {
// bgGradient='linear(to-r, #ffd54f, #caf5d8)' // bgGradient='linear(to-r, #ffd54f, #caf5d8)'
// bgGradient='linear(to-r, #caf5d8, #a8e6cf)' // bgGradient='linear(to-r, #caf5d8, #a8e6cf)'
boxShadow={"md"} boxShadow={"md"}
paddingRight={"10px"}
borderRadius={"10px"}
position={"relative"} position={"relative"}
> >
<Box h={100} w={200} p={1.5}> <HStack gap={4}>
{/* <Image rounded={'md'} h={"100%"} src={ " https://tanami.betadelivery.com/" + IODetails?.ioName} alt={IODetails?.ioName}/> */} <Box h={100} w={200} p={1.5}>
{IODetails?.artifactsImage?.[0]?.artifactPathName ? ( {/* <Image rounded={'md'} h={"100%"} src={ " https://tanami.betadelivery.com/" + IODetails?.ioName} alt={IODetails?.ioName}/> */}
<Image {IODetails?.artifactsImage?.[0]?.artifactPathName ? (
rounded={"md"} <Image
h={"100%"} rounded={"md"}
w={"100%"} h={"100%"}
objectFit={"cover"} w={"100%"}
src={ objectFit={"cover"}
" https://tanami.betadelivery.com/" + src={
IODetails?.artifactsImage?.[0]?.artifactPathName import.meta.env.VITE_IMAGE_URL +
} IODetails?.artifactsImage?.[0]?.artifactPathName
alt={IODetails?.ioName} }
/> alt={IODetails?.ioName}
) : ( />
<Box ) : (
w={"100%"} <Box
h={"100%"} w={"100%"}
display={"flex"} h={"100%"}
justifyContent={"center"} display={"flex"}
alignItems={"center"} justifyContent={"center"}
bg={"#fff"} alignItems={"center"}
rounded={"md"} bg={"#fff"}
> rounded={"md"}
<Icon color={"gray.700"} as={GrGallery} /> >
<Icon color={"gray.700"} as={GrGallery} />
</Box>
)}
</Box>
<Box>
<Box display={"flex"} gap={2} pb={1}>
<Text
as={"span"}
fontSize={"xs"}
color={"gray.500"}
fontWeight={"500"}
me={2}
>
IO Name :-
</Text>
<Text as={"span"} fontSize={"xs"} fontWeight={"500"}>
{IODetails?.investmentNameEnglish
? IODetails?.investmentNameEnglish
: "---"}
</Text>
</Box> </Box>
)}
</Box>
{/* <Box display={"flex"} flexDirection={"column"} gap={2}>
<Text as={"span"} fontSize={"sm"} fontWeight={"500"}>
IO name: <Text as={'span'} ms={2}>{foundObject?.ioName}</Text>
</Text>
<Text as={"span"} fontSize={"sm"} fontWeight={"500"}>
Sponsor name: <Text as={'span'} ms={2}>{foundObject?.sponserName}</Text>
</Text>
</Box> */}
{/* <Box display={"flex"} flexDirection={"column"} gap={2}> <Box display={"flex"} gap={2} pb={1}>
<Text as={"span"} fontSize={"xs"} color={"gray.500"} fontWeight={"500"}> <Text
IO ID as={"span"}
</Text> fontSize={"xs"}
<Text as={"span"} fontSize={"sm"} fontWeight={"500"}> color={"gray.500"}
{IODetails?.io_id ? IODetails?.io_id : "---"} fontWeight={"500"}
</Text> me={2}
</Box> */} >
Sponsor Name :-
</Text>
<Text as={"span"} fontSize={"xs"} fontWeight={"500"}>
{IODetails?.sponsor?.sponsorName
? IODetails?.sponsor?.sponsorName
: "---"}
</Text>
</Box>
<Box display={"flex"} w={"auto"} flexDirection={"column"} gap={2}> <Box display={"flex"} gap={2} pb={1}>
<Text as={"span"} fontSize={"xs"} color={"gray.500"} fontWeight={"500"}> <Text
IO Name as={"span"}
</Text> fontSize={"xs"}
<Text as={"span"} fontSize={"sm"} fontWeight={"500"}> color={"gray.500"}
{IODetails?.investmentNameEnglish fontWeight={"500"}
? IODetails?.investmentNameEnglish me={2}
: "---"} >
</Text> IO ID :-
</Text>
<Text as={"span"} fontSize={"xs"} fontWeight={"500"}>
{IODetails?.io_id
? IODetails?.io_id
: "---"}
</Text>
</Box>
</Box>
</HStack>
<Box gap={8} me={12} w={"220px"}>
<Box display={"flex"} justifyContent={"space-between"} gap={2} pb={1}>
<Text
as={"span"}
fontSize={"xs"}
color={"gray.500"}
fontWeight={"500"}
>
Goal Amount :-
</Text>
<Text as={"span"} fontSize={"xs"} fontWeight={"500"}>
{/* {IODetails?.ioNAV ? formatCurrency(removeTrailingZeros(IODetails?.ioNAV)) : "00.00"} */}
{parseFloat(IODetails?.goalAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</Box>
<Box display={"flex"} justifyContent={"space-between"} gap={2} pb={1}>
<Text
as={"span"}
fontSize={"xs"}
color={"gray.500"}
fontWeight={"500"}
>
Amount Raised :-
</Text>
<Text as={"span"} fontSize={"xs"} fontWeight={"500"}>
{/* {IODetails?.ioCash ? formatCurrency(removeTrailingZeros(IODetails?.ioCash)) : "00.00"} */}
{parseFloat(IODetails?.totalAmtInvestmentInUSD || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</Box>
<Box display={"flex"} justifyContent={"space-between"} gap={2}>
<Text
as={"span"}
fontSize={"xs"}
color={"gray.500"}
fontWeight={"500"}
>
Balance :-
</Text>
<Text as={"span"} fontSize={"xs"} fontWeight={"500"}>
{/* {IODetails?.ioMVNAV ? formatCurrency(removeTrailingZeros(IODetails?.ioMVNAV)) : "00.00"} */}
{parseFloat(balanceAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</Box>
</Box> </Box>
<Box display={"flex"} flexDirection={"column"} gap={2}> <Box gap={8} me={12} w={"180px"}>
<Text as={"span"} fontSize={"xs"} color={"gray.500"} fontWeight={"500"}> <Box display={"flex"} justifyContent={"space-between"} gap={2} pb={1}>
Sponsorer Name <Text
</Text> as={"span"}
<Text as={"span"} fontSize={"sm"} fontWeight={"500"}> textAlign={"center"}
{IODetails?.sponsor?.sponsorName fontSize={"xs"}
? IODetails?.sponsor?.sponsorName color={"gray.500"}
: "---"} fontWeight={"500"}
</Text> >
</Box> IO Status :-
</Text>
<Badge
rounded={"full"}
pt={0}
pb={0.5}
textTransform={"none"}
// variant={"solid"}
colorScheme={
IODetails?.ioStatus?.statusAdmin === "Draft"
? "gray"
: IODetails?.ioStatus?.statusAdmin === "Processing"
? "yellow"
: IODetails?.ioStatus?.statusAdmin === "Open"
? "blue"
: IODetails?.ioStatus?.statusAdmin === "Closed"
? "green"
: IODetails?.ioStatus?.statusAdmin === "Exited"
? "red"
: IODetails?.ioStatus?.statusAdmin === "Cancelled"
? "purple"
: "purple"
}
>
{IODetails?.ioStatus?.statusAdmin
? IODetails?.ioStatus?.statusAdmin
: "---"}
</Badge>
</Box>
<Box display={"flex"} flexDirection={"column"} gap={2}> <Box display={"flex"} justifyContent={"space-between"} gap={2} pb={1}>
<Text as={"span"} fontSize={"xs"} color={"gray.500"} fontWeight={"500"}> <Text
IO Status as={"span"}
</Text> fontSize={"xs"}
<Badge color={"gray.500"}
rounded={"sm"} fontWeight={"500"}
pt={0.5} >
pb={0.5} IO MV :-
ps={4} </Text>
pe={4} <Text as={"span"} fontSize={"xs"} fontWeight={"500"}>
textTransform={"none"} {/* {IODetails?.ioNAV ? formatCurrency(removeTrailingZeros(IODetails?.ioNAV)) : "00.00"} */}
variant={"solid"} {parseFloat(IODetails?.ioNAV || 0).toLocaleString(undefined, {
color={"#fff"} minimumFractionDigits: 2,
colorScheme={ maximumFractionDigits: 2,
IODetails?.ioStatus?.statusAdmin === "Draft" })}
? "blue" </Text>
: IODetails?.ioStatus?.statusAdmin === "Pending" </Box>
? "purple"
: "forestGreen" <Box display={"flex"} justifyContent={"space-between"} gap={2} pb={1}>
} <Text
> as={"span"}
{IODetails?.ioStatus?.statusAdmin fontSize={"xs"}
? IODetails?.ioStatus?.statusAdmin color={"gray.500"}
: "---"} fontWeight={"500"}
</Badge> >
</Box> IO cash :-
<Box display={"flex"} flexDirection={"column"} gap={2}> </Text>
<Text as={"span"} fontSize={"xs"} color={"gray.500"} fontWeight={"500"}> <Text as={"span"} fontSize={"xs"} fontWeight={"500"}>
IO NAV {/* {IODetails?.ioCash ? formatCurrency(removeTrailingZeros(IODetails?.ioCash)) : "00.00"} */}
</Text> {parseFloat(IODetails?.ioCash || 0).toLocaleString(undefined, {
<Text as={"span"} fontSize={"sm"} fontWeight={"500"}> minimumFractionDigits: 2,
{IODetails?.currentValuation ? IODetails?.currentValuation : "00.00"} maximumFractionDigits: 2,
</Text> })}
</Box> </Text>
<Box display={"flex"} flexDirection={"column"} gap={2}> </Box>
<Text as={"span"} fontSize={"xs"} color={"gray.500"} fontWeight={"500"}>
IO cash <Box display={"flex"} justifyContent={"space-between"} gap={2}>
</Text> <Text
<Text as={"span"} fontSize={"sm"} fontWeight={"500"}> as={"span"}
{IODetails?.ioCash ? IODetails?.ioCash : "00.00"} fontSize={"xs"}
</Text> color={"gray.500"}
</Box> fontWeight={"500"}
<Box display={"flex"} flexDirection={"column"} gap={2}> >
<Text as={"span"} fontSize={"xs"} color={"gray.500"} fontWeight={"500"}> IO NAV :-
IO MV NAV </Text>
</Text> <Text as={"span"} fontSize={"xs"} fontWeight={"500"}>
<Text as={"span"} fontSize={"sm"} fontWeight={"500"}> {/* {IODetails?.ioMVNAV ? formatCurrency(removeTrailingZeros(IODetails?.ioMVNAV)) : "00.00"} */}
{IODetails?.marketValue ? IODetails?.marketValue : "00.00"} {parseFloat(IODetails?.ioMVNAV || 0).toLocaleString(undefined, {
</Text> minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</Box>
</Box> </Box>
<Box <Box
@@ -272,114 +465,19 @@ const ViewIOdataHeader = () => {
> >
Tansaction Tansaction
</MenuItem> </MenuItem>
<MenuItem onClick={onInvestmentOpen} className="border-bottom">
Amount Invested {filteredMenu?.map(({ id, title, onClickFunction }) => (
</MenuItem> <MenuItem
<MenuItem onClick={onFeesOpen} className="border-bottom"> key={id}
Fees & Expenses onClick={onClickFunction}
</MenuItem> className="border-bottom"
<MenuItem onClick={onDistSponsorOpen} className="border-bottom"> >
Distribution from Sponsors {title}
</MenuItem> </MenuItem>
<MenuItem onClick={onDistInvestorOpen} className="border-bottom"> ))}
Distribution To investors
</MenuItem>
<MenuItem onClick={onUpdateNavOpen} className="border-bottom">
Update iO NAV
</MenuItem>
<MenuItem onClick={onExitOpen} className="border-bottom">
Exit
</MenuItem>
<MenuItem onClick={onCancleOpen} className="border-bottom">
Cancel
</MenuItem>
<MenuItem onClick={onUpdateStatusOpen}>Update iO status</MenuItem>
</MenuList> </MenuList>
</Menu> </Menu>
{/* Drawer */}
{/* <Drawer
isOpen={isOpen}
placement="right"
onClose={onClose}
finalFocusRef={btnRef}
>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader>Transaction</DrawerHeader>
<DrawerBody>
<Box
display={"grid"}
alignContent={"left"}
justifyItems={"start"}
>
<Button
onClick={onInvestmentOpen}
bg={bg}
_hover={hover}
paddingInline={"0px"}
>
Amount Invested
</Button>
<Divider />
<Button
onClick={onFeesOpen}
bg={bg}
_hover={hover}
paddingInline={"0px"}
>
Fees & Expenses
</Button>
<Divider />
<Button
onClick={onDistSponsorOpen}
bg={bg}
_hover={hover}
paddingInline={"0px"}
>
Distribution from Sponsors
</Button>
<Divider />
<Button
onClick={onDistInvestorOpen}
bg={bg}
_hover={hover}
paddingInline={"0px"}
>
Distribution To Investors
</Button>
<Divider />
<Button
onClick={onUpdateNavOpen}
bg={bg}
_hover={hover}
paddingInline={"0px"}
>
Update IO NAV
</Button>
<Divider />
<Button
onClick={onUpdateStatusOpen}
bg={bg}
_hover={hover}
paddingInline={"0px"}
>
Update IO Status
</Button>
<Divider />
</Box>
</DrawerBody>
<DrawerFooter>
<Button variant="outline" mr={3} onClick={onClose}>
Cancel
</Button>
</DrawerFooter>
</DrawerContent>
</Drawer> */}
{/* Modals */} {/* Modals */}
<AmountInvested isOpen={isInvestmentOpen} onClose={onInvestmentClose} /> <AmountInvested isOpen={isInvestmentOpen} onClose={onInvestmentClose} />
<FeesExpenses isOpen={isFeesOpen} onClose={onFeesClose} /> <FeesExpenses isOpen={isFeesOpen} onClose={onFeesClose} />
@@ -393,13 +491,34 @@ const ViewIOdataHeader = () => {
isOpen={isDistInvestorOpen} isOpen={isDistInvestorOpen}
onClose={onDistInvestorClose} onClose={onDistInvestorClose}
/> />
<UpdateIONav isOpen={isUpdateNavOpen} onClose={onUpdateNavClose} /> <UpdateIONav isOpen={isUpdateNavOpen} onClose={onUpdateNavClose} />
<UpdateIOStatus <UpdateIOStatus
status={IODetails?.nextStatus}
isOpen={isUpdateStatusOpen} isOpen={isUpdateStatusOpen}
onClose={onUpdateStatusClose} onClose={onUpdateStatusClose}
/> />
</Box> </Box>
</Box> </Box>
) : (
<Box
display={"flex"}
alignItems={"center"}
justifyContent={"center"}
rounded={"md"}
height={100}
bg={"#fff"}
// bgGradient='linear(to-r, #caf5d8, #f5e8ca)'
// bgGradient='linear(to-r, #caf5d8, #d4a5a5)'
// bgGradient='linear(to-r, #caf5d8, #d4a5a5)'
// bgGradient='linear(to-r, #caf5d8, #b3e5fc)'
// bgGradient='linear(to-r, #ffd54f, #caf5d8)'
// bgGradient='linear(to-r, #caf5d8, #a8e6cf)'
boxShadow={"md"}
>
{" "}
<Loader01 />
</Box>
); );
}; };

View File

@@ -11,6 +11,8 @@ import { useGetIOByIdQuery } from "../../../Services/io.service";
import * as yup from "yup"; import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup"; import { yupResolver } from "@hookform/resolvers/yup";
import { formatDate } from "../../Master/Sponser/Sponsers"; import { formatDate } from "../../Master/Sponser/Sponsers";
import { formatCurrency } from "../../../Components/CurrencyInput";
import { removeTrailingZeros } from "../../../Constants/Constants";
const schema = yup.object().shape({ const schema = yup.object().shape({
investmentNameEnglish: yup investmentNameEnglish: yup
@@ -71,7 +73,7 @@ const schema = yup.object().shape({
.positive("Expected return must be a positive number") .positive("Expected return must be a positive number")
.min(0.01, "Expected return must be at least 0.01"), .min(0.01, "Expected return must be at least 0.01"),
}); });
const ViewIOdetails = () => { const ViewIOdetails = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const params = useParams(); const params = useParams();
@@ -104,31 +106,33 @@ const ViewIOdetails = () => {
investmentNameArabic: IObyID?.data?.investmentNameArabic, investmentNameArabic: IObyID?.data?.investmentNameArabic,
descriptionEnglish: IObyID?.data?.descriptionEnglish, descriptionEnglish: IObyID?.data?.descriptionEnglish,
descriptionArabic: IObyID?.data?.descriptionArabic, descriptionArabic: IObyID?.data?.descriptionArabic,
goalAmount: IObyID?.data?.goalAmount, goalAmount: removeTrailingZeros(IObyID?.data?.goalAmount),
closingDate: IObyID?.data?.closingDate, closingDate: IObyID?.data?.closingDate,
holdingPeriod: IObyID?.data?.holdingPeriod, holdingPeriod: IObyID?.dpata?.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,
}); });
} }
}, [id, IObyID]); }, [id, IObyID]);
console.log(IObyID);
const minInvestmentById = IObyID?.data?.minInvestmentAmt?.map(({minInvestmentAmt, country, id})=>{ const minInvestmentById = IObyID?.data?.minInvestmentAmt?.map(({minInvestmentAmt, country, currencyCode, id})=>{
return{ return{
id:id, id:id,
country: country?.countryName, country: country?.countryName,
value: minInvestmentAmt, value: removeTrailingZeros(minInvestmentAmt),
logo: country?.flagIcon, logo: country?.flagIcon,
curr: country?.countryCode, curr: currencyCode,
} }
}) })
console.log();
//=======================[ Editor ] //=======================[ Editor ]
const formFields = [ const formFields = [
{ {
@@ -167,7 +171,7 @@ const ViewIOdetails = () => {
}, },
{ {
label: "Description (Arabic)", label: "Description (Arabic)",
name: "descriptionArabic", name: "descriptionArabic",
value: IObyID?.data?.descriptionArabic value: IObyID?.data?.descriptionArabic
? IObyID?.data?.descriptionArabic ? IObyID?.data?.descriptionArabic
: "---", : "---",
@@ -177,6 +181,70 @@ const ViewIOdetails = () => {
section: " ", section: " ",
width: "49%", width: "49%",
}, },
{
label: "Holding Period",
name: "holdingPeriod",
value: IObyID?.data?.holdingPeriod ? IObyID?.data?.holdingPeriod : "---",
type: "text",
isRequired: true,
placeHolder: "1Y",
section: " ",
width: "49%",
},
{
label: "Holding Period (Arabic)",
name: "holdingPeriodArabic",
value: IObyID?.data?.holdingPeriodArabic ? IObyID?.data?.holdingPeriodArabic : "---",
type: "text",
isRequired: true,
arabic: true,
placeHolder: "1Y",
section: " ",
width: "49%",
},
{
label: "Expected Return",
placeHolder: "$00.00",
name: "expectedReturn",
type: "number",
isRequired: true,
value: IObyID?.data?.expectedReturn
? IObyID?.data?.expectedReturn
: "---",
section: " ",
width: "32.3%",
},
{
label: "Expected Return (Arabic)",
name: "expectedReturnArabic",
placeHolder: "$00.00",
type: "number",
isRequired: true,
arabic: true,
value: IObyID?.data?.expectedReturnArabic
? IObyID?.data?.expectedReturnArabic
: "---",
section: " ",
width: "32.3%",
},
{
label: "Shariah",
name: "isShariah",
type: "checkBox",
// isRequired: true,
section: " ",
width: "32.3%",
value: IObyID?.data?.isShariah,
},
{ {
label: "Investment Type", label: "Investment Type",
placeHolder: "Select option", placeHolder: "Select option",
@@ -190,7 +258,7 @@ const ViewIOdetails = () => {
width: "32.3%", width: "32.3%",
}, },
{ {
label: "Sponsorer Name", label: "Sponsor Name",
placeHolder: "Select option", placeHolder: "Select option",
name: "sponsor_xid", name: "sponsor_xid",
type: "select", type: "select",
@@ -204,10 +272,12 @@ const ViewIOdetails = () => {
{ {
label: "Goal Amount", label: "Goal Amount",
placeHolder: "$00.00", placeHolder: "$00.00",
value: IObyID?.data?.goalAmount ? IObyID?.data?.goalAmount : "---", value: IObyID?.data?.goalAmount?parseFloat(IObyID?.data?.goalAmount||0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) : "---",
name: "goalAmount", name: "goalAmount",
type: "number", type: "number",
isRequired: true, isRequired: true,
arabic:true,
section: " ", section: " ",
width: "32.3%", width: "32.3%",
}, },
@@ -222,28 +292,6 @@ const ViewIOdetails = () => {
section: " ", section: " ",
width: "32.3%", width: "32.3%",
}, },
{
label: "Holding Period",
name: "holdingPeriod",
value: IObyID?.data?.holdingPeriod ? IObyID?.data?.holdingPeriod : "---",
type: "text",
isRequired: true,
placeHolder: "1Y",
section: " ",
width: "32.3%",
},
{
label: "Expected Return",
placeHolder: "$00.00",
name: "expectedReturn",
type: "number",
isRequired: true,
value: IObyID?.data?.expectedReturn
? IObyID?.data?.expectedReturn
: "---",
section: " ",
width: "32.3%",
},
{ {
label: "ISIN", label: "ISIN",
placeHolder: "$00.00", placeHolder: "$00.00",
@@ -274,7 +322,6 @@ const ViewIOdetails = () => {
section: " ", section: " ",
width: "100%", width: "100%",
isRequired: true, isRequired: true,
type: "table",
value: minInvestmentById, value: minInvestmentById,
}, },
@@ -299,7 +346,7 @@ const ViewIOdetails = () => {
}, {}); }, {});
if (!IObyID?.data) { if (!IObyID?.data) {
return <FullscreenLoaders />; return <FullscreenLoaders height={'70vh'} />;
} }
return ( return (
@@ -316,8 +363,8 @@ const ViewIOdetails = () => {
size={"xs"} size={"xs"}
> >
Edit IO Edit IO
</Button>{" "} </Button>
<FormInputView groupedFields={groupedFields} />{" "} <FormInputView groupedFields={groupedFields} />
</Box> </Box>
); );
}; };

View File

@@ -0,0 +1,245 @@
import {
Badge,
Box,
Button,
HStack,
Input,
Text,
Tooltip,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import {
Link,
Link as RouterLink,
useNavigate,
useParams,
} from "react-router-dom";
import { AddIcon, DeleteIcon, EditIcon, ViewIcon } 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 {
useDeleteInvestmentTypeMutation,
useGetInvestmentTypesQuery,
} from "../../../Services/io.service";
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
import { generateSerialNumber } from "../../../Constants/Constants";
import { useGetInvestorsDetailsByIdQuery } from "../../../Services/investor.details.service";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
const BankDetails = () => {
const params = useParams();
const id = params?.id;
const navigate = useNavigate();
const toast = useToast();
// ======================== [Use State] =========================
const [searchTerm, setSearchTerm] = useState("");
// const [isLoading, setIsLoading] = useState(false);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const { investmentType, setInvestmentType, slideFromRight } =
useContext(GlobalStateContext);
const [deleteInvestmentType] = useDeleteInvestmentTypeMutation();
// =========================== [Use State] =============================
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
// const {
// data: investmentTypes,
// isLoading: investmentTypesLoading,
// error,
// } = useGetInvestmentTypesQuery({ page: currentPage, size: pageSize });
const { data, isLoading:bankDetailsLoading, errors, refetch } = useGetInvestorsDetailsByIdQuery(
id,
{
skip: !id,
}
);
console.log(data?.data?.bank_details);
const filteredData = data?.data?.bank_details?.filter((item) => {
const name = item.bankNickName;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches;
});
// ==================================================== [Table Setup] ================================================================
const tableHeadRow = [
"Sr No",
"Bank Nick Name",
"IBAN",
"Account No",
"Bank Name",
"Account Name",
"Bank Address",
"SWIFT Code",
];
const extractedArray = filteredData?.map((item, idx) => ({
"Sr No": (
<Text
w={"24px"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center fw-bold web-text-small"
>
{/* {item.id} */}
{generateSerialNumber(idx, currentPage, pageSize)}
</Text>
),
"Bank Nick Name": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
isTruncated={true}
className="d-flex align-items-center web-text-small"
>
{item.bankNickName}
</Text>
),
IBAN: (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
isTruncated={true}
className="d-flex align-items-center web-text-small"
>
{item.IBANnumber}
</Text>
),
"Account No": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
isTruncated={true}
className="d-flex align-items-center web-text-small"
>
{item.accountNumber}
</Text>
),
"Bank Name": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
isTruncated={true}
className="d-flex align-items-center web-text-small"
>
{item.bankName}
</Text>
),
"Account Name": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
isTruncated={true}
className="d-flex align-items-center web-text-small"
>
{item.accountName}
</Text>
),
"Bank Address": (
<Text
w={"200px"} // Set a width to constrain the text
justifyContent={slideFromRight ? "right" : "left"}
whiteSpace={"normal"} // Allow wrapping
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.bankAddress}
</Text>
),
"SWIFT Code": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
isTruncated={true}
className="d-flex align-items-center web-text-small"
>
{item.swiftCode}
</Text>
),
}));
// ==================== [Delete Function] =======================
const handleDelete = async () => {
console.log(actionId);
setIsLoading(true);
try {
const response = await deleteInvestmentType(actionId);
console.log(response);
setIsLoading(false);
setDeleteAlert(false);
} catch (error) {}
};
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<HStack>
<Text as={'span'} fontSize={'sm'} fontWeight={700}>Bank Deatils</Text>
</HStack>
<HStack
display={"flex"}
justifyContent={"space-between"}
ps={1}
pe={1}
pb={4}
pt={4}
spacing="24px"
>
{/* ======================= [Search Input] ======================== */}
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</HStack>
{/* ======================== [Data Table] ======================= */}
<NormalTable
emptyMessage={`We don't have any Investment type `}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={bankDetailsLoading}
viewActionId={actionId}
setViewActionId={setActionId}
/>
</Box>
);
};
export default BankDetails;

View File

@@ -5,11 +5,6 @@ import {
Button, Button,
HStack, HStack,
Input, Input,
Menu,
MenuButton,
MenuItem,
MenuList,
Portal,
Select, Select,
Switch, Switch,
Tag, Tag,
@@ -21,13 +16,8 @@ import {
import React, { useContext, useEffect, useState, useRef } from "react"; import React, { useContext, useEffect, useState, useRef } from "react";
import { OPACITY_ON_LOAD } from "../../../Layout/animations"; import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import NormalTable from "../../../Components/DataTable/NormalTable"; import NormalTable from "../../../Components/DataTable/NormalTable";
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,
EmailIcon,
ViewIcon, ViewIcon,
} from "@chakra-ui/icons"; } from "@chakra-ui/icons";
import Pagination from "../../../Components/Pagination"; import Pagination from "../../../Components/Pagination";
@@ -38,6 +28,8 @@ import { debounce } from "../../Master/Sponser/AddSponser";
import InvestmentDetailsEdit from "./InvestmentDetailsEdit"; import InvestmentDetailsEdit from "./InvestmentDetailsEdit";
import { useGetInvestorsQuery } from "../../../Services/investor.details.service"; import { useGetInvestorsQuery } from "../../../Services/investor.details.service";
import { TABLE_PAGINATION } from "../../../Constants/Paginations"; import { TABLE_PAGINATION } from "../../../Constants/Paginations";
import { exportToExcel, exportToExcelNew, generateSerialNumber } from "../../../Constants/Constants";
import { LuFileSpreadsheet } from "react-icons/lu";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
@@ -47,7 +39,6 @@ const InvestorDetails = () => {
const thirdField = useRef(); const thirdField = useRef();
const { InvestorDetails, setInvestorDetails, 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);
@@ -60,15 +51,44 @@ const InvestorDetails = () => {
} = useDisclosure(); } = useDisclosure();
const btnRef = React.useRef(); const btnRef = React.useRef();
// =========================== [Use State] =============================
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size); const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page); 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 { const {
data: investorDetails, data: investorDetails,
isLoading: investorDetailsLoading, isLoading: investorDetailsLoading,
error, error,
} = useGetInvestorsQuery({ page: currentPage, size: pageSize }); } = useGetInvestorsQuery({
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search userStatus KYCStatus investorType_xid
search: debouncedSearchTerm,
userStatus: status,
KYCStatus: kyc,
country_xid: country
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
}
);
console.log(investorDetails);
useEffect(() => { useEffect(() => {
// Simulate loading // Simulate loading
@@ -82,61 +102,48 @@ const InvestorDetails = () => {
// ====================================================[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",
"Investor Type", "Type",
"KYC Status", "KYC Status",
"Status", "Status",
"Action", "Action",
]; ];
const handleUpdateStatus = debounce((id) => {
setInvestorDetails((prevData) =>
prevData.map((InvestorDetails) =>
InvestorDetails.id === id ? { ...InvestorDetails } : InvestorDetails
)
);
toast({
render: () => <ToastBox message={"Status changed succesfully.!"} />,
});
}, 300);
// ====================================================[Table Filter]================================================================ // ====================================================[Table Filter]================================================================
const filteredData = investorDetails?.data?.rows?.filter((item) => { const exportInvestor = investorDetails?.data?.rows?.map((item, idx) => ({
// Filter by name (case insensitive) "Id": parseInt(item?.id, 10) || item?.id, // Convert to integer, fallback to string if conversion fails
const name = item.clientReference_id; "Client ID": item?.clientReference_id, // This is likely a string
const searchLower = searchTerm.toLowerCase(); "First Name": item?.principal?.firstName,
const nameMatches = name?.toLowerCase().includes(searchLower); "Last Name": item?.principal?.lastName,
"Country": item?.country?.countryName,
"Phone Number": item?.principal?.mobileNumber, // Skipping integer conversion, as this is likely a string
"E-mail ID": item?.principal?.emailAddress,
"Type": item?.investor_type?.investorTypeName,
"Status": item.ioStatus ? "Ban" : "Unban",
"KYC Status": item.KYCStatus ? "Completed" : "Not complete"
}));
// Filter by status const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({
// const status = item.status;
// const statusLower = status ? "active" : "inactive";
// const statusMatches =
// statusFilter === "all" ||
// (statusFilter === "active" && status === true) ||
// (statusFilter === "inactive" && status === false);
return nameMatches;
});
console.log(investorDetails);
const extractedArray = filteredData?.map((item) => ({
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": (
@@ -144,7 +151,7 @@ const InvestorDetails = () => {
<Text as={"span"} color={"teal.900"}> <Text as={"span"} color={"teal.900"}>
{item.clientReference_id} {item.clientReference_id}
</Text> </Text>
</Box> </Box>
), ),
"First Name": ( "First Name": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
@@ -154,7 +161,7 @@ const InvestorDetails = () => {
</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?.principal?.lastName} {item?.principal?.lastName}
</Text> </Text>
@@ -181,10 +188,10 @@ const InvestorDetails = () => {
</Text> </Text>
</Box> </Box>
), ),
"Investor Type": ( "Type": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"purple"}> <Text as={"span"} >
<Badge colorScheme="purple" variant={'solid'} fontWeight={"500"} px={2} py={0.5}> <Badge color={"forestGreen.500"} variant={'ghost'} fontWeight={"700"} px={2} py={0.5}>
{item?.investor_type?.investorTypeName} {item?.investor_type?.investorTypeName}
</Badge> </Badge>
</Text> </Text>
@@ -193,29 +200,30 @@ const InvestorDetails = () => {
Status: ( Status: (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Badge <Badge
fontWeight={"500"} fontWeight={"700"}
// textTransform={"none"} textTransform={"none"}
colorScheme={item.ioStatus ? "red" : "green"} colorScheme={item.ioStatus ? "red" : "green"}
px={2} px={2}
py={0.5} py={0.5}
variant={'solid'}
> >
{item.ioStatus ? "Ban" : "Unban"} {item.ioStatus ? "Ban" : "Unban"}
</Badge> </Badge>
</Box> </Box>
), ),
"KYC Status": ( "KYC Status": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} display={'flex'} alignItems={'center'} isTruncated={true}>
<Badge <Text
fontWeight={"500"} as={'span'}
// textTransform={"none"} fontWeight={"700"}
colorScheme={item.kycStatus ? "blue" : "red"} textTransform={"none"}
color={item.ioStatus ? "gray.500":item.kycStatus ? "blue.500" : "red.500"}
px={2} px={2}
py={0.5} py={0.5}
variant={'solid'} variant={'solid'}
> >
{item.KYCStatus ? "Completed" : "Not complete"} {item.KYCStatus ? "Completed" : "Not complete"}
</Badge> </Text>
</Box> </Box>
), ),
Action: ( Action: (
@@ -229,6 +237,7 @@ const InvestorDetails = () => {
placement="top" placement="top"
> >
<Button <Button
isDisabled={item.ioStatus}
onClick={() => { onClick={() => {
navigate(`/investor-details/profile-view/${item.id}`); navigate(`/investor-details/profile-view/${item.id}`);
}} }}
@@ -259,10 +268,6 @@ const InvestorDetails = () => {
setIsLoading(true); setIsLoading(true);
}; };
const handleEdit = (id) => {
setActionId(id);
onEditOpen();
};
return ( return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}> <Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
@@ -294,14 +299,16 @@ const InvestorDetails = () => {
size={"sm"} size={"sm"}
fontSize={"xs"} fontSize={"xs"}
cursor={"pointer"} cursor={"pointer"}
onChange={(e) => setStatus(e.target.value)}
value={status}
> >
<option value="" defaultValue={"all"} disabled hidden> <option value="" defaultValue={""} disabled hidden>
Status Status
</option> </option>
<option value="all">All</option> <option value="">Status</option>
<option value="ban">Ban</option> <option value="0">Ban</option>
<option value="unban">UnBan</option> <option value="1">UnBan</option>
</Select> </Select>
<Select <Select
@@ -309,43 +316,15 @@ const InvestorDetails = () => {
size={"sm"} size={"sm"}
fontSize={"xs"} fontSize={"xs"}
cursor={"pointer"} cursor={"pointer"}
onChange={(e) => setKyc(e.target.value)}
value={kyc}
> >
<option value="" defaultValue={"all"} disabled hidden> <option value="" defaultValue={""} disabled hidden>
Status
</option>
<option value="all">All</option>
<option value="ban">Ban</option>
<option value="unban">UnBan</option>
</Select>
<Select
focusBorderColor="green.500"
size={"sm"}
fontSize={"xs"}
cursor={"pointer"}
>
<option value="" defaultValue={"all"} disabled hidden>
Status
</option>
<option value="all">All</option>
<option value="ban">Ban</option>
<option value="unban">UnBan</option>
</Select>
<Select
focusBorderColor="green.500"
size={"sm"}
fontSize={"xs"}
cursor={"pointer"}
>
<option value="" defaultValue={"all"} disabled hidden>
KYC Status KYC Status
</option> </option>
<option value="all">All</option> <option value="">KYC Status</option>
<option value="completed">Completed</option> <option value="0">Incompleted</option>
<option value="incompleted">Incompleted</option> <option value="1">Completed</option>
</Select> </Select>
<Select <Select
@@ -353,26 +332,51 @@ const InvestorDetails = () => {
size={"sm"} size={"sm"}
fontSize={"xs"} fontSize={"xs"}
cursor={"pointer"} cursor={"pointer"}
onChange={(e) => setCountry(e.target.value)}
value={country}
> >
<option value="" defaultValue={"all"} disabled hidden> <option value="" defaultValue={""} disabled hidden>
Country Country
</option> </option>
<option value="behrain">Behrain</option> <option value="">All</option>
<option value="kuwait">Kuwait</option> <option value="1">Behrain</option>
<option value="oman">Oman</option> <option value="2">Kuwait</option>
<option value="saudi arabia">Saudi arabia</option> <option value="3">Oman</option>
<option value="united arab emirates">United arab emirates</option> <option value="4">Qatar</option>
<option value="5">Saudi arabia</option>
<option value="6">United arab emirates</option>
</Select> </Select>
<Pagination <Pagination
isLoading={investorDetailsLoading} isLoading={investorDetailsLoading}
pageSize={pageSize} pageSize={pageSize}
setPageSize={setPageSize} setPageSize={setPageSize}
currentPage={currentPage} currentPage={currentPage}
setCurrentPage={setCurrentPage} setCurrentPage={setCurrentPage}
totalItems={investorDetails?.data?.totalItems} totalItems={investorDetails?.data?.totalItems}
/> />
</HStack> </HStack>
<Button
onClick={() =>
exportToExcelNew(exportInvestor, "Investor Details")
}
leftIcon={<LuFileSpreadsheet />}
colorScheme="forestGreen"
size={"sm"}
variant={"outline"}
rounded={"sm"}
fontSize={"xs"}
w={100}
me={2}
isDisabled={exportInvestor?.length === 0}
>
Export xls
</Button>
</HStack> </HStack>
<InvestmentDetailsEdit <InvestmentDetailsEdit
id={actionId} id={actionId}

View File

@@ -0,0 +1,216 @@
import {
Box,
Checkbox,
CheckboxGroup,
Divider,
FormControl,
FormLabel,
Heading,
HStack,
Input,
Radio,
RadioGroup,
Select,
Stack,
Text,
Textarea,
} from "@chakra-ui/react";
import React, { useState } from "react";
import { useParams } from "react-router-dom";
import { useGetInvestorsDetailsByIdQuery } from "../../../Services/investor.details.service";
const Kyc = () => {
const params = useParams();
const id = params?.id;
const {
data,
isLoading: bankDetailsLoading,
errors,
refetch,
} = useGetInvestorsDetailsByIdQuery(id, { skip: !id });
// const [value, setValue] = useState('2')
console.log(data?.data?.KYC?.questions);
return (
!bankDetailsLoading && (
<Box>
<Box mb={5}>
{/*<Heading fontSize={"md"} fontWeight={500}>
Additional Questions
</Heading>
<Text fontSize={"sm"}>
Please provide additional Information to complete your profile.
</Text> */}
<Heading fontSize={"md"} fontWeight={500}>
Address
</Heading>
<Divider />
<HStack spacing={4} mb={4}>
<FormControl>
<FormLabel mb={1} fontSize={"sm"}>
House/Unit
</FormLabel>
<Input
bg={"#ccc3"}
border={"none"}
size={"sm"}
value={data?.data?.KYC?.otherInformation?.houseOrUnit}
type="text"
readOnly
/>
</FormControl>
<FormControl>
<FormLabel mb={1} fontSize={"sm"}>
Road/Street
</FormLabel>
<Input
bg={"#ccc3"}
border={"none"}
size={"sm"}
value={data?.data?.KYC?.otherInformation?.roadOrStreet}
type="text"
readOnly
/>
</FormControl>
</HStack>
<HStack spacing={4} mb={5}>
<FormControl>
<FormLabel mb={1} fontSize={"sm"}>
Block
</FormLabel>
<Input
bg={"#ccc3"}
border={"none"}
size={"sm"}
value={data?.data?.KYC?.otherInformation?.block}
type="text"
readOnly
/>
</FormControl>
<FormControl>
<FormLabel mb={1} fontSize={"sm"}>
City
</FormLabel>
<Input
bg={"#ccc3"}
border={"none"}
size={"sm"}
value={data?.data?.KYC?.otherInformation?.city}
type="text"
readOnly
/>
</FormControl>
</HStack>
<HStack spacing={4} mb={7}>
<FormControl>
<FormLabel mb={1} fontSize={"sm"}>
Country
</FormLabel>
<Input
bg={"#ccc3"}
border={"none"}
size={"sm"}
value={data?.data?.KYC?.otherInformation?.country}
type="text"
readOnly
/>
</FormControl>
<FormControl></FormControl>
</HStack>
<Heading fontSize={"md"} fontWeight={500}>
Additional Questions
</Heading>
<Divider />
<HStack spacing={4} mb={4}>
<FormControl>
<FormLabel mb={1} fontSize={"sm"}>
Occupation
</FormLabel>
<Input
bg={"#ccc3"}
border={"none"}
size={"sm"}
value={data?.data?.KYC?.otherInformation?.occupation}
type="text"
readOnly
/>
</FormControl>
<FormControl>
<FormLabel mb={1} fontSize={"sm"}>
Source of Funds
</FormLabel>
<Input
bg={"#ccc3"}
border={"none"}
size={"sm"}
value={data?.data?.KYC?.otherInformation?.sourceOfFund}
type="text"
readOnly
/>
</FormControl>
</HStack>
{/* <HStack spacing={4}>
<FormControl>
<FormLabel mb={1} fontSize={"sm"}>Address</FormLabel>
<Textarea bg={"#ccc3"} border={"none"} size={"sm"} type="textarea" resize={"none"} value={data?.data?.KYC?.otherInformation?.address} readOnly />
</FormControl>
<FormControl></FormControl>
</HStack> */}
</Box>
<Box>
<Heading fontSize={"md"} fontWeight={500}>
Investor Eligibilty Notice
</Heading>
<Text fontSize={"sm"}>
Please note that Tanami is currently only available to qualifield or
accredited investors. Please confirm your status
</Text>
<RadioGroup>
{data?.data?.KYC?.questions.map((question, index) => (
<>
<Radio
isChecked={question?.answers}
colorScheme="forestGreen"
key={index}
>
<Text fontSize={"sm"} mb={0}>
{question?.question_text}
</Text>
</Radio>
<CheckboxGroup colorScheme="forestGreen">
<Stack ml={8}>
{question?.subQuestions?.map((subQuestion, index) => (
<Checkbox
isChecked={subQuestion.answers}
size="sm"
key={index}
>
<Text as="span">{subQuestion.question_text}</Text>
</Checkbox>
))}
</Stack>
</CheckboxGroup>
</>
))}
</RadioGroup>
{/* {value === '2' && (
<CheckboxGroup colorScheme='forestGreen'>
<Stack ml={8}>
{data?.data?.KYC?.questions[1]?.subQuestions?.map((subQuestion, index) => (
<Checkbox isChecked={subQuestion.answers} size="sm" key={index}>
<Text as="span">{subQuestion.question_text}</Text>
</Checkbox>
))}
</Stack>
</CheckboxGroup>
)} */}
</Box>
</Box>
)
);
};
export default Kyc;

View File

@@ -1,6 +1,7 @@
import { import {
Badge, Badge,
Box, Box,
Divider,
Icon, Icon,
Tab, Tab,
TabList, TabList,
@@ -19,31 +20,47 @@ import FormInputView from "../../../Components/FormInputView";
import ViewInvestorDetails from "./ViewInvestorDetails"; import ViewInvestorDetails from "./ViewInvestorDetails";
import { LuWallet } from "react-icons/lu"; import { LuWallet } from "react-icons/lu";
import Transaction from "./Transaction"; import Transaction from "./Transaction";
import FullscreenLoaders from '../../../Components/Loaders/FullscreenLoaders' import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
import { useGetInvestorsDetailsByIdQuery } from "../../../Services/investor.details.service"; import { useGetInvestorsDetailsByIdQuery } from "../../../Services/investor.details.service";
import BankDetails from "./BankDetails";
import Kyc from "./Kyc";
const ProfileView = () => { const ProfileView = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const params = useParams(); const params = useParams();
const { InvestorDetails } = useContext(GlobalStateContext); const {
InvestorDetails,
setViewInvestor,
setTransaction,
InvestorWallet,
setInvestorWallet,
} = useContext(GlobalStateContext);
const { reset } = useForm(); // assuming react-hook-form const { reset } = useForm(); // assuming react-hook-form
const id = params?.id; const id = params?.id;
const { data, isLoading, errors } = useGetInvestorsDetailsByIdQuery(id, { const { data, isLoading, errors, refetch} = useGetInvestorsDetailsByIdQuery(id, {
skip: !id, skip: !id,
}); });
console.log(data?.data);
const foundObject = data?.data; // Use useEffect to refetch data when the component mounts
useEffect(() => {
refetch();
}, [refetch]);
const foundObject = data?.data;
useEffect(() => {
setViewInvestor(data?.data?.portfolio);
setTransaction(data?.data?.transaction);
setInvestorWallet(data?.data.wallet);
}, [data]);
const formFields = [ const formFields = [
{ {
label: "Client ID", label: "Client ID",
value: foundObject?.id, value: foundObject?.clientReference_id,
type: "text", type: "text",
isRequired: true, isRequired: true,
section: "Personal Details", section: "Personal Details",
@@ -51,7 +68,7 @@ const ProfileView = () => {
}, },
{ {
label: "First Name", label: "First Name",
value: foundObject?.principal?.firstName, value: foundObject?.firstName,
type: "text", type: "text",
isRequired: true, isRequired: true,
section: "Personal Details", section: "Personal Details",
@@ -60,7 +77,7 @@ const ProfileView = () => {
{ {
label: "Last Name", label: "Last Name",
value: foundObject?.principal?.lastName, value: foundObject?.lastName,
type: "text", type: "text",
isRequired: true, isRequired: true,
section: "Personal Details", section: "Personal Details",
@@ -68,7 +85,7 @@ const ProfileView = () => {
}, },
{ {
label: "E-mail ID", label: "E-mail ID",
value: foundObject?.principal?.emailAddress, value: foundObject?.emailAddress,
type: "text", type: "text",
isRequired: true, isRequired: true,
section: "Personal Details", section: "Personal Details",
@@ -76,7 +93,7 @@ const ProfileView = () => {
}, },
{ {
label: "Phone Number", label: "Phone Number",
value: foundObject?.principal?.mobileNumber, value: foundObject?.mobileNumber,
type: "tel", type: "tel",
isRequired: true, isRequired: true,
section: "Personal Details", section: "Personal Details",
@@ -84,7 +101,7 @@ const ProfileView = () => {
}, },
{ {
label: "Country", label: "Country",
value: foundObject?.country?.countryName, value: foundObject?.country ? foundObject?.country : "---",
type: "text", type: "text",
isRequired: true, isRequired: true,
section: "Personal Details", section: "Personal Details",
@@ -92,23 +109,23 @@ const ProfileView = () => {
}, },
{ {
label: "Investment type", label: "Investment type",
value: foundObject?.investor_type?.investorTypeName, value: foundObject?.investorTypeName,
type: "text",
isRequired: true,
section: "Personal Details",
width: "32%",
},
{
label: "Investor limit",
value: foundObject?.investorLimit,
type: "text", type: "text",
isRequired: true, isRequired: true,
section: "Personal Details", section: "Personal Details",
width: "32%", width: "32%",
}, },
// {
// label: "Investor limit",
// value: foundObject?.investorLimit,
// type: "text",
// isRequired: true,
// section: "Personal Details",
// width: "32%",
// },
{ {
label: "KYC Status", label: "KYC Status",
value: foundObject?.KYCStatus ? "Completed" : "Not complete", value: foundObject?.KYCStatus ? "Completed" : "Not complete",
type: "text", type: "text",
isRequired: true, isRequired: true,
section: "Personal Details", section: "Personal Details",
@@ -124,7 +141,7 @@ const ProfileView = () => {
}, },
{ {
label: "Default language", label: "Default language",
value: foundObject?.defaultLanguage_xid === 1 ? "English" : "Arabic" , value: foundObject?.defaultLanguage_xid === 1 ? "English" : "Arabic",
type: "text", type: "text",
isRequired: true, isRequired: true,
section: "Personal Details", section: "Personal Details",
@@ -132,12 +149,60 @@ const ProfileView = () => {
}, },
{ {
label: "App Notification", label: "App Notification",
value: foundObject?.IsAppNotificationEnabled === 1 ? "Yes" : "No" , value: foundObject?.IsAppNotificationEnabled === true ? "Yes" : "No",
type: "text", type: "text",
isRequired: true, isRequired: true,
section: "Personal Details", section: "Personal Details",
width: "32%", width: "32%",
}, },
// {
// label: "Account Name",
// value: foundObject?.accountName,
// type: "text",
// isRequired: true,
// section: "Bank Details",
// width: "32%",
// },
// {
// label: "Account No",
// value: foundObject?.accountNumber,
// type: "text",
// isRequired: true,
// section: "Bank Details",
// width: "32%",
// },
// {
// label: "IBAN",
// value: foundObject?.IBANnumber,
// type: "text",
// isRequired: true,
// section: "Bank Details",
// width: "32%",
// },
// {
// label: "SWIFT Code",
// value: foundObject?.swiftCode,
// type: "text",
// isRequired: true,
// section: "Bank Details",
// width: "32%",
// },
// {
// label: "Bank Name",
// value: foundObject?.bankName,
// type: "text",
// isRequired: true,
// section: "Bank Details",
// width: "32%",
// },
// {
// label: "Bank Address",
// value: foundObject?.bankAddress,
// type: "text",
// isRequired: true,
// section: "Bank Details",
// width: "32%",
// },
]; ];
const groupedFields = formFields.reduce((groups, field) => { const groupedFields = formFields.reduce((groups, field) => {
@@ -149,8 +214,9 @@ const ProfileView = () => {
return groups; return groups;
}, {}); }, {});
return ( return isLoading ? (
isLoading? <FullscreenLoaders />: <FullscreenLoaders />
) : (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={14}> <Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={14}>
<Box <Box
pt={2} pt={2}
@@ -191,10 +257,10 @@ const ProfileView = () => {
display={"flex"} display={"flex"}
> >
<Text fontWeight={500} as={"span"}> <Text fontWeight={500} as={"span"}>
26,763.40 {parseFloat(InvestorWallet?.WalletBalance_InUSD || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</Text> </Text>
<Badge display={"flex"} alignItems={"center"}> <Badge display={"flex"} alignItems={"center"}>
USD {InvestorWallet?.currencyCode_USD}
</Badge> </Badge>
</Box> </Box>
</Box> </Box>
@@ -211,10 +277,10 @@ const ProfileView = () => {
display={"flex"} display={"flex"}
> >
<Text fontWeight={500} as={"span"}> <Text fontWeight={500} as={"span"}>
10,075.01 {parseFloat(InvestorWallet?.WalletBalance_InInvCur || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</Text> </Text>
<Badge display={"flex"} alignItems={"center"}> <Badge display={"flex"} alignItems={"center"}>
BHD {InvestorWallet?.currencyCode_InCur}
</Badge> </Badge>
</Box> </Box>
</Box> </Box>
@@ -238,6 +304,7 @@ const ProfileView = () => {
View Details View Details
</Tab> </Tab>
<Tab <Tab
// isDisabled={true}
fontSize={"sm"} fontSize={"sm"}
_selected={{ _selected={{
color: "#004118", color: "#004118",
@@ -247,6 +314,7 @@ const ProfileView = () => {
Portfolio Portfolio
</Tab> </Tab>
<Tab <Tab
// isDisabled={true}
fontSize={"sm"} fontSize={"sm"}
_selected={{ _selected={{
color: "#004118", color: "#004118",
@@ -256,19 +324,22 @@ const ProfileView = () => {
Transaction Transaction
</Tab> </Tab>
<Tab <Tab
// isDisabled={true}
fontSize={"sm"} fontSize={"sm"}
_selected={{ _selected={{
color: "#004118", color: "#004118",
borderBottom: "2px solid #38a169", borderBottom: "2px solid #38a169",
}} }}
> >
documents KYC
</Tab> </Tab>
</TabList> </TabList>
</Box> </Box>
<TabPanels> <TabPanels>
<TabPanel> <TabPanel>
<FormInputView width={"32%"} groupedFields={groupedFields} /> <FormInputView width={"32%"} groupedFields={groupedFields} />
<Divider/>
<BankDetails />
</TabPanel> </TabPanel>
<TabPanel> <TabPanel>
<ViewInvestorDetails /> <ViewInvestorDetails />
@@ -276,6 +347,9 @@ const ProfileView = () => {
<TabPanel> <TabPanel>
<Transaction /> <Transaction />
</TabPanel> </TabPanel>
<TabPanel>
<Kyc />
</TabPanel>
</TabPanels> </TabPanels>
</Tabs> </Tabs>
</Box> </Box>

View File

@@ -1,374 +1,312 @@
import { import {
Avatar, Avatar,
Badge, Badge,
Box, Box,
Button, Button,
HStack, HStack,
Input, Input,
Menu, Menu,
MenuButton, MenuButton,
MenuItem, MenuItem,
MenuList, MenuList,
Portal, Portal,
Select, Select,
Switch, Switch,
Table, Table,
Tag, Tag,
Tbody, Tbody,
Text, Text,
Th, Th,
Tooltip, Tooltip,
Tr, Tr,
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 { HiDotsVertical } from "react-icons/hi"; import Pagination from "../../../Components/Pagination";
import { Link, Navigate, Link as RouterLink } from "react-router-dom"; import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import { import CustomAlertDialog from "../../../Components/CustomAlertDialog";
AddIcon, import ToastBox from "../../../Components/ToastBox";
DeleteIcon, import { debounce } from "../../Master/Sponser/AddSponser";
EditIcon,
EmailIcon,
ViewIcon,
} from "@chakra-ui/icons";
import Pagination from "../../../Components/Pagination";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import ToastBox from "../../../Components/ToastBox";
import { debounce } from "../../Master/Sponser/AddSponser";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
const Transaction = () => {
const toast = useToast();
const { transaction, setTransaction, 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 N/O",
"Date",
"Transaction",
"Currency",
"Amount",
"From USD",
"TO USD",
"USD amount",
"IO Name",
"Payment Method",
];
const handleUpdateStatus = debounce((id) => {
setViewInvestor((prevData) =>
prevData.map((transaction) =>
transaction.id === id ? { ...transaction } : transaction
)
);
toast({
render: () => <ToastBox message={"Status changed succesfully.!"} />,
});
}, 300);
// ====================================================[Table Filter]================================================================
const filteredData = transaction?.filter((item) => {
// Filter by name (case insensitive)
const name = item.date;
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 w={'50px'}
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"}>
{item.date}
</Text>
</Box>
),
"Transaction": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.transaction}
</Text>
</Box>
),
"Currency": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.currency}
</Text>
</Box>
),
"Amount": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.amount}
</Text>
</Box>
),
"From USD": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.fromUSD}
</Text>
</Box>
),
"TO USD": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.toUSD}
</Text>
</Box>
),
"USD amount": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.USDamount}
</Text>
</Box>
),
"IO Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.IOName}
</Text>
</Box>
),
"Payment Method": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.paymentMethod}
</Text>
</Box>
),
}));
const handleDelete = () => {
const updatedInvestorDetails = InvestorDetails.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setInvestorDetails(updatedInvestorDetails);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const Total = () => { const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
return (
<Table size="sm"> const Transaction = () => {
<Tbody backgroundColor="gray.50"> const toast = useToast();
<Tr > const { transaction, setTransaction, slideFromRight, InvestorWallet } =
<Th useContext(GlobalStateContext);
textAlign={"center"} const [searchTerm, setSearchTerm] = useState("");
p={3} const [isLoading, setIsLoading] = useState(true);
width="60px" const [deleteAlert, setDeleteAlert] = useState(false);
color={"#004118"} const [actionId, setActionId] = useState(false);
whiteSpace="normal" const [mouseEntered, setMouseEntered] = useState(false);
wordBreak="normal" const [mouseEnteredId, setMouseEnteredId] = useState("");
overflowWrap="normal"
> useEffect(() => {
Balance: // Simulate loading
</Th> const timer = setTimeout(() => {
<Th setIsLoading(false);
textAlign={"center"} }, 1500);
p={3}
width="60px" // Cleanup the timer on component unmount
color={"#004118"} return () => clearTimeout(timer);
whiteSpace="normal" }, []);
wordBreak="normal"
overflowWrap="normal" const formatDate = (date) => {
> return new Date(date).toLocaleDateString("en-GB", {
{" "} day: "2-digit",
</Th> month: "2-digit",
<Th year: "numeric",
textAlign={"center"} });
p={3}
width="60px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{" "}
</Th>
<Th
textAlign={"center"}
p={3}
width="60px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{" "}
</Th>
<Th
textAlign={"center"}
p={3}
width="80px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
10,075.01
</Th>
<Th
textAlign={"center"}
p={3}
width="60px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{" "}
</Th>
<Th
textAlign={"center"}
p={3}
width="60px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{" "}
</Th>
<Th
textAlign={"center"}
p={3}
width="80px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
26,763.40
</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>
</Tr>
</Tbody>
</Table>
);
};
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)}
/>
<HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} />
</HStack>
</HStack>
</Box>
<DataTable
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
setMouseEnteredId={setMouseEnteredId}
caption={<Total />}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
</Box>
);
}; };
export default Transaction; console.log(transaction);
// ====================================================[Table Filter]================================================================
const filteredData = transaction?.filter((item) => {
// Filter by name (case insensitive)
console.log(item?.investorTransaction?.transactionName);
const name = item?.investorTransaction?.transactionName;
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;
});
console.log(filteredData);
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
// "Sr N/O",
"Date",
"Transaction",
"Amount",
// "Currency",
// "TO USD",
// "From USD",
// "USD amount",
"IO Name",
"Payment Method",
];
const extractedArray = filteredData?.map((item) => ({
id: item?.id,
"Sr N/O": (
<Text
w={"100px"}
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={"80px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{formatDate(item.transactionDate)}
</Text>
</Box>
),
Transaction: (
<Box w={"80px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.investorTransaction?.transactionName}
</Text>
</Box>
),
Currency: (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.investorCurrency}
</Text>
</Box>
),
Amount: (
<Box w={100} display={"flex"} justifyContent={"right"}>
<Text
as={"span"}
w={"100%"}
color={"teal.900"}
display={"flex"}
justifyContent={"end"}
>
{/* <Badge ms={1} colorScheme="green" me={1}>$</Badge> */}
{parseFloat(item?.investorAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
<Badge ms={1} colorScheme="green">
{item?.investorCurrency}
</Badge>
</Text>
</Box>
),
"From USD": (
<Text as={"span"} color={"teal.900"}>
{item.USDToInvCur_Rate}
</Text>
),
"TO USD": (
<Text as={"span"} color={"teal.900"}>
{item.invCurToUSD_Rate}
</Text>
),
// "USD amount": (
// <Box w={100} display={"flex"} justifyContent={"left"}>
// {item?.invCurToUSD_Rate === "0.0000" && (
// <Text as={"span"} w={"100%"} color={"teal.900"}>
// <Badge ms={1} colorScheme="green" me={1}>
// $
// </Badge>
// {parseFloat(item?.USDAmount || 0).toLocaleString(undefined, {
// minimumFractionDigits: 2,
// maximumFractionDigits: 2,
// })}
// </Text>
// )}
// </Box>
// ),
"IO Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.io && item.io?.investmentNameEnglish}
</Text>
</Box>
),
"Payment Method": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.paymentMethod && item.paymentMethod?.paymentMethodName}
</Text>
</Box>
),
}));
const totalRow = {
// id: "total", // or any unique ID
// "Sr N/O": (
// <Text w={'100px'} color={"gray.600"} fontWeight={"bold"}>Total</Text>
// ),
Date: (
<Text
as={"span"}
textAlign={"start"}
w={"100%"}
color={"teal.900"}
fontWeight={"bold"}
>
<Badge ms={1} colorScheme="gray">
Total
</Badge>
</Text>
),
Transaction: null,
// "Currency": null,
Amount: InvestorWallet?.currencyCode_InCur && (
<Box w={100} display={"flex"} justifyContent={"end"}>
<Text
as={"span"}
textAlign={"right"}
w={"100%"}
color={"teal.900"}
fontWeight={600}
>
{/* <Badge ms={1} colorScheme="green" me={1}>$</Badge> */}
{parseFloat(
InvestorWallet?.WalletBalance_InInvCur || 0
).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
<Badge ms={1} colorScheme="green">
{InvestorWallet?.currencyCode_InCur}
</Badge>
</Text>
</Box>
),
// "TO USD": null,
// "From USD": null,
"USD amount": null,
"IO Name": null,
"Payment Method": null,
};
extractedArray?.push(totalRow);
const handleDelete = () => {
const updatedInvestorDetails = InvestorDetails.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setInvestorDetails(updatedInvestorDetails);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
return (
<Box {...OPACITY_ON_LOAD} 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)}
/>
{/* <HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} />
</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}
/>
</Box>
);
};
export default Transaction;

View File

@@ -19,9 +19,14 @@ 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 { HiDotsVertical } from "react-icons/hi"; import { HiDotsVertical } from "react-icons/hi";
import { Link, Navigate, Link as RouterLink } from "react-router-dom"; import {
Link,
Navigate,
Link as RouterLink,
useParams,
} from "react-router-dom";
import { import {
AddIcon, AddIcon,
DeleteIcon, DeleteIcon,
@@ -34,12 +39,13 @@ 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 { useGetInvestorsDetailsById_InInvCurQuery } from "../../../Services/investor.details.service";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
const ViewInvestorDetails = () => { const ViewInvestorDetails = () => {
const toast = useToast(); const toast = useToast();
const { viewInvestor, setViewInvestor, slideFromRight } = const { viewInvestor, setViewInvestor, slideFromRight, InvestorWallet } =
useContext(GlobalStateContext); useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
@@ -47,6 +53,16 @@ const ViewInvestorDetails = () => {
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 [isSwitchOn, setIsSwitchOn] = useState(false);
const params = useParams();
const id = params?.id;
const { data, errors, refetch } = useGetInvestorsDetailsById_InInvCurQuery(
{ id, currencyIn: !isSwitchOn },
{
skip: !id,
}
);
useEffect(() => { useEffect(() => {
// Simulate loading // Simulate loading
@@ -60,31 +76,27 @@ const ViewInvestorDetails = () => {
// ====================================================[Table Setup]================================================================ // ====================================================[Table Setup]================================================================
const tableHeadRow = [ const tableHeadRow = [
"Sr N/O", // "Sr N/O",
"Deal Name", // "Deal Name",
"IO Name",
"Sponsor Name", "Sponsor Name",
"Investment Type",
"Investment Amount", "Investment Amount",
"Holding Period", "Percentage",
"Estimated return", "Market Value",
"Return on Investment",
"Distribution",
"Distribution Percent",
"Total return",
"Total return on Investment",
"Status", "Status",
"Action", // "Action",
]; ];
const handleUpdateStatus = debounce((id) => {
setViewInvestor((prevData) =>
prevData.map((viewInvestor) =>
viewInvestor.id === id ? { ...viewInvestor } : viewInvestor
)
);
toast({
render: () => <ToastBox message={"Status changed succesfully.!"} />,
});
}, 300);
// ====================================================[Table Filter]================================================================ // ====================================================[Table Filter]================================================================
const filteredData = viewInvestor?.filter((item) => { const filteredData = viewInvestor?.filter((item) => {
// Filter by name (case insensitive) // Filter by name (case insensitive)
const name = item.dealId; const name = item.sponsorName;
const searchLower = searchTerm.toLowerCase(); const searchLower = searchTerm.toLowerCase();
const nameMatches = name?.toLowerCase().includes(searchLower); const nameMatches = name?.toLowerCase().includes(searchLower);
@@ -100,22 +112,23 @@ const ViewInvestorDetails = () => {
return nameMatches; return nameMatches;
}); });
const extractedArray = filteredData?.map((item) => ({ const extractedArray = filteredData?.map((item, index) => ({
id: item?.id, id: item?.id,
"Sr N/O": ( "Sr N/O": (
<Text w={'50px'} <Text
w={"50px"}
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} {index + 1}.
</Text> </Text>
), ),
"Deal Name": ( "IO Name": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}> <Text as={"span"} color={"teal.900"}>
{item.dealName} {item.investmentNameEnglish}
</Text> </Text>
</Box> </Box>
), ),
@@ -126,45 +139,205 @@ const ViewInvestorDetails = () => {
</Text> </Text>
</Box> </Box>
), ),
"Investment Type": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.investmentTypeName}
</Text>
</Box>
),
"Investment Amount": ( "Investment Amount": (
<Box w={"auto"} isTruncated={true}> <Box
display={"flex"}
justifyContent={isSwitchOn ? "end" : "start"}
w={"auto"}
isTruncated={true}
>
<Text as={"span"} color={"teal.900"}> <Text as={"span"} color={"teal.900"}>
{item.investAmount} {isSwitchOn ? (
<>
{parseFloat(item?.investedAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
<Badge ms={1} colorScheme="green" me={1}>
{InvestorWallet?.currencyCode_InCur}
</Badge>
</>
) : (
<>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(item?.investedAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</>
)}
</Text> </Text>
</Box> </Box>
), ),
"Holding Period": ( Percentage: (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}> <Text as={"span"} color={"teal.900"}>
{item.holdingPeriod} {item?.Investor_Holidings} %
</Text> </Text>
</Box> </Box>
), ),
"Estimated return": ( "Market Value": (
<Box
display={"flex"}
justifyContent={isSwitchOn ? "end" : "start"}
w={"auto"}
isTruncated={true}
>
<Text as={"span"} color={"teal.900"}>
{isSwitchOn ? (
<>
{parseFloat(item?.MarketValue || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
<Badge ms={1} colorScheme="green" me={1}>
{InvestorWallet?.currencyCode_InCur}
</Badge>
</>
) : (
<>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(item?.MarketValue || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</>
)}
</Text>
</Box>
),
"Return on Investment": (
<Box w={"auto"} isTruncated={true}> <Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}> <Text as={"span"} color={"teal.900"}>
{item.estimatedReturn} {item.MarketValue_Per} %
</Text>
</Box>
),
Distribution: (
<Box
display={"flex"}
justifyContent={isSwitchOn ? "end" : "start"}
w={"auto"}
isTruncated={true}
>
<Text as={"span"} color={"teal.900"}>
{isSwitchOn ? (
<>
{parseFloat(item?.DistributionAmountReceived || 0).toLocaleString(
undefined,
{
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}
)}
<Badge ms={1} colorScheme="green" me={1}>
{InvestorWallet?.currencyCode_InCur}
</Badge>
</>
) : (
<>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(item?.DistributionAmountReceived || 0).toLocaleString(
undefined,
{
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}
)}
</>
)}
</Text>
</Box>
),
"Distribution Percent": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.Distribution_Per} %
</Text>
</Box>
),
"Total return": (
<Box
display={"flex"}
justifyContent={isSwitchOn ? "end" : "start"}
w={"auto"}
isTruncated={true}
>
<Text as={"span"} color={"teal.900"}>
{isSwitchOn ? (
<>
{parseFloat(item?.TotalReturn || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
<Badge ms={1} colorScheme="green" me={1}>
{InvestorWallet?.currencyCode_InCur}
</Badge>
</>
) : (
<>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(item?.TotalReturn || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</>
)}
</Text>
</Box>
),
"Total return on Investment": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.TotalReturn_Per} %
</Text> </Text>
</Box> </Box>
), ),
Status: ( Status: (
<Box w={"auto"} isTruncated={true}> <Badge
<Badge rounded={"full"}
fontWeight={"500"} pt={1}
textTransform={"none"} pb={1}
color={ ps={4}
item.kycStatus === "Open" pe={4}
? "green" mt={1.5}
: item.kycStatus === "Pending" mb={1.5}
? "blue" textTransform={"none"}
: "red" // variant={"solid"}
} colorScheme={
px={2} item?.statusAdmin === "Draft"
py={0.5} ? "gray"
> : item?.statusAdmin === "Processing"
{item.kycStatus} ? "yellow"
</Badge> : item?.statusAdmin === "Open"
</Box> ? "blue"
: item?.statusAdmin === "Closed"
? "green"
: item?.statusAdmin === "Exited"
? "red"
: item?.statusAdmin === "Canclled"
? "orange"
: "purple"
}
boxShadow={"0 4px 6px rgba(0, 0, 0, 0.1)"} // Adjusted shadow
>
{item?.statusAdmin}
</Badge>
), ),
Action: ( Action: (
<Box display={"flex"} justifyContent={"space-between"}> <Box display={"flex"} justifyContent={"space-between"}>
@@ -203,6 +376,11 @@ const ViewInvestorDetails = () => {
setIsLoading(true); setIsLoading(true);
}; };
const switchOnChangeHandle = () => {
setIsSwitchOn(!isSwitchOn); // Toggle the switch state
setViewInvestor(data?.data?.portfolio);
};
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">
@@ -225,14 +403,54 @@ const ViewInvestorDetails = () => {
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
/> />
<HStack display={"flex"} alignItems={"center"}> {/*
<Box
as="button"
display="flex"
alignItems="center"
justifyContent={isSwitchOn ? "flex-end" : "flex-start"} // Use this if needed
width="155px"
height="30px"
borderRadius="20px"
backgroundColor={isSwitchOn ? "#004118" : "#ef0000"}
onClick={switchOnChangeHandle} // Correct handler name
position="relative"
fontSize="13px"
fontWeight="100"
transition="background-color 0.2s"
_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(128px)" : "translateX(0)",
transition: "transform 0.2s",
left: "4px",
}}
>
<Text
fontWeight="400"
zIndex={1}
position="absolute"
mb={0}
color="#fff"
left={isSwitchOn ? "10px" : "auto"}
right={isSwitchOn ? "auto" : "10px"}
>
{isSwitchOn ? "Investor Currency" : "Investor Currency"}
</Text>
</Box> */}
{/* <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}
@@ -250,7 +468,6 @@ const ViewInvestorDetails = () => {
alertHandler={handleDelete} alertHandler={handleDelete}
isLoading={isLoading} isLoading={isLoading}
/> />
</Box> </Box>
); );
}; };

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