Compare commits

..

275 Commits

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

View File

@@ -2,7 +2,7 @@
<html lang="en">
<head>
<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" />
<title>Tanami Admin</title>
</head>
@@ -22,7 +22,7 @@ const craftedMsg = "Crafted with ❤️ by WDI Team for a better web.";
</script>
<script type="text/javascript">
<!-- <script type="text/javascript">
function googleTranslateElementInit() {
new google.translate.TranslateElement({
pageLanguage: 'en',
@@ -30,7 +30,10 @@ const craftedMsg = "Crafted with ❤️ by WDI Team for a better web.";
layout: google.translate.TranslateElement.InlineLayout.SIMPLE
}, 'google_translate_element');
}
</script>
<script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
</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>
</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",
"@hookform/resolvers": "^3.3.4",
"@reduxjs/toolkit": "^2.2.3",
"apexcharts": "^3.52.0",
"axios": "^1.7.2",
"bootstrap": "5.3.3",
"chart.js": "^4.4.3",
"dotenv": "^16.4.5",
"framer-motion": "^11.1.5",
"js-cookie": "^3.0.5",
"quill": "^2.0.2",
"react": "^18.2.0",
"react-apexcharts": "^1.4.1",
"react-beautiful-dnd": "^13.1.1",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.51.3",
"react-icons": "^5.1.0",
"react-phone-input-2": "^2.15.1",
"react-quill": "^2.0.0",
"react-redux": "^9.1.1",
"react-router-dom": "^6.22.3",

View File

@@ -14,6 +14,11 @@
/* font-family: "Lato", sans-serif !important; */
}
::selection {
background-color: #004717; /* Change this to your desired color */
color: white; /* Optional: Change the text color */
}
.pointer {
cursor: pointer !important;
}
@@ -336,3 +341,151 @@
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,
Route,
Navigate,
useNavigate,
} from "react-router-dom";
import "./App.css"; // Import CSS file
import DefaultLayout from "./Layout/DefaultLayout";
@@ -14,6 +15,8 @@ import Login from "./Pages/Login";
import GlobalStateContext from "./Contexts/GlobalStateContext";
import Cookies from "js-cookie";
import NoInternetScreen from "./Pages/NoInternetScreen";
import Welcome from "./Pages/PaymentSuccess";
import PaymentFailed from "./Pages/PaymentFailed";
const App = () => {
// const { isAuthenticate } = useSelector((state) => state?.auth);
@@ -24,6 +27,8 @@ const App = () => {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
const handleOnlineStatusChange = () => {
setIsOnline(navigator.onLine);
};
@@ -37,22 +42,30 @@ const App = () => {
};
}, []);
const PrivateRoute = ({ children }) => {
if (!isAuthenticate && isAuthenticatedInCookie !== "true") {
return <Navigate to="/login" replace />;
}
return children;
};
// const token = localStorage.getItem('accessToken')
// console.log(token);
// const PrivateRoute = ({ children }) => {
// if (!isAuthenticate && isAuthenticatedInCookie !== "true") {
// return <Navigate to="/login" replace />;
// }
// return children;
// };
return (
<Router>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/payment-success" element={<Welcome />} />
<Route path="/payment-failed" element={<PaymentFailed />} />
<Route
path="/*"
element={
// isOnline ? (
isAuthenticate || isAuthenticatedInCookie === "true" ? (
// isAuthenticate || isAuthenticatedInCookie === "true" ? (
localStorage.getItem('accessToken') && localStorage.getItem('refreshToken') ? (
// true ? (
<DefaultLayout isOnline={isOnline} />
) : (
<Login />

View File

@@ -151,7 +151,7 @@ const CreateIO = () => {
}, []);
const tableHeadRow = [
"Sponsorer name",
"Sponsor name",
"Address",
"Mobile no",
"Created At",
@@ -191,7 +191,7 @@ const CreateIO = () => {
const extractedArray = filteredData?.map((item) => ({
id: item?.id,
"Sponsorer name": (
"Sponsor name": (
<Text
justifyContent={"left"}
as={"span"}
@@ -413,7 +413,7 @@ const CreateIO = () => {
],
},
{
label: "Sponsorer Name (English)",
label: "Sponsor Name (English)",
placeHolder: " ",
name: "sponserName",
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);
reorderedItems.splice(result.destination.index, 0, removed);
setData(reorderedItems)
console.log("New Order:", reorderedItems.map((item, index) => ({ index, item })));
// console.log("New Order:", reorderedItems.map((item, index) => ({ index, item })));
};
return (
@@ -65,7 +65,7 @@ const DataTable = ({
{/* <Box mb={2}>{caption}</Box> */}
<Table size="sm" {...provided.droppableProps} ref={provided.innerRef}>
<TableCaption p={0}>{caption}</TableCaption>
<Thead backgroundColor="gray.50">
<Thead backgroundColor="forestGreen.100">
<Tr>
{tableHeadRow.map((heading, index) => (
<Th
@@ -97,7 +97,7 @@ const DataTable = ({
bg: "blue.50",
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"}
>
@@ -121,7 +121,7 @@ const DataTable = ({
)}
</Draggable>
) : (
<Tr key={index}>
<Tr bg={index % 2 ? "forestGreen.50" : "white"} key={index}>
{tableHeadRow.map((heading, i) => (
<Td
textAlign={tableHeadRow.length - 1 === i || centered ? "center" : "left"}

View File

@@ -9,70 +9,171 @@ import {
Tr,
Skeleton,
TableCaption,
Tfoot,
Checkbox,
Radio,
} from "@chakra-ui/react";
import EmptySearchList from "../EmptySearchList";
import { TABLE_PAGINATION } from "../../Constants/Paginations";
const DataTable = ({
const NormalTable = ({
data,
isLoading,
tableHeadRow,
emptyMessage,
centered,
total,
showRadioButton, // Prop for showing the checkboxes
selectedRadio,
setSelectedRadio, // State for managing selected checkboxes
handleCheckboxChange: radioChange,
radio
}) => {
const columnWidth =
data && data[0]
? `${(100 / Object.keys(data[0]).length).toFixed(2)}%`
: "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 (
<TableContainer overflowX={"hidden"} className="h-auto mb-3 w-100">
<TableContainer overflowX={"auto"} className="h-auto w-100 table-scroll">
{data?.length === 0 ? (
<EmptySearchList message={emptyMessage} />
) : (
<Table size="sm">
<TableCaption>Tanami v1.0.0</TableCaption>
<Thead backgroundColor="gray.50">
<TableCaption p={total ? 0 : null}>
{total ? total : "Tanami v1.0.0"}
</TableCaption>
<Thead bg="forestGreen.100">
<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) => (
<Th
width={'fit-content'}
color={"purple.900"}
textAlign={
tableHeadRow.length - 1 === index || centered
? "center"
: "left"
}
key={index}
p={3}
w={columnWidth}
p={4}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
textTransform={"none"}
>
{isLoading ? <Skeleton height="20px" /> : heading}
{/* {heading} */}
</Th>
))}
</Tr>
</Thead>
<Tbody className="web-text-small">
{isLoading
? Array.from({ length: 12 }).map((_, index) => (
<Tr key={index}>
{tableHeadRow.map((_, i) => (
<Td
width={'fit-content'}
key={i}
style={{
whiteSpace: "nowrap",
textOverflow: "ellipsis",
}}
className="web-text-small"
w={columnWidth}
>
<Skeleton height="20px" mb={1} mt={1} />
</Td>
))}
</Tr>
))
? Array.from({ length: TABLE_PAGINATION?.size }).map(
(_, index) => (
<Tr
bg={index % 2 === 0 ? "white" : "forestGreen.50"}
key={index}
>
{tableHeadRow.map((_, i) => (
<Td
key={i}
style={{
whiteSpace: "nowrap",
textOverflow: "ellipsis",
}}
className="web-text-small"
>
<Skeleton height="20px" mb={1} mt={1} />
</Td>
))}
</Tr>
)
)
: 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) => (
<Td
textAlign={
@@ -82,6 +183,7 @@ const DataTable = ({
}
color={"gray.600"}
key={i}
fontWeight={500}
style={{
whiteSpace: "nowrap",
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,
InputGroup,
InputRightAddon,
HStack,
} from "@chakra-ui/react";
import { Controller } from "react-hook-form";
import { TiWarning } from "react-icons/ti";
import { motion } from "framer-motion";
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 formatDate = (dateString) => {
export const formatDatee = (dateString) => {
const date = new Date(dateString);
const year = date.getFullYear();
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";
// Format the default date as YYYY-MM-DD
const formattedDate = formatDate(defaultDate);
const formattedDate = formatDatee(defaultDate);
const FormField = ({
label,
@@ -67,12 +70,14 @@ const FormField = ({
handleInputChange,
align,
maxLength,
dateValue,
closingDate,
...props
}) => (
<FormControl
w={width ? width : "49%"}
isInvalid={errors[name]}
isRequired={isRequired}
isRequired={type === "date" ? true: isRequired}
mb={2}
>
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
@@ -82,6 +87,7 @@ const FormField = ({
control={control}
name={name}
defaultValue={value}
rules={rules}
render={({ field }) => {
if (type === "select") {
@@ -402,8 +408,7 @@ const FormField = ({
w={6}
h={6}
src={
" https://tanami.betadelivery.com/" +
item?.logo
import.meta.env.VITE_IMAGE_URL+item?.logo
}
/>
{item.country === "United Arab Emirates"
@@ -460,7 +465,65 @@ const FormField = ({
</Tbody>
</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 (
<Input
bg={"#F5F8F6"}
@@ -474,7 +537,7 @@ const FormField = ({
placeholder={placeHolder ? placeHolder : label}
textAlign={arabic || type === "number" ? "right" : align ? align : "left"}
_placeholder={{ fontSize: "sm" }}
min={type === "date" ? today : undefined}
// min={type === "date" ? today : undefined}
maxLength={maxLength}
// defaultValue={type === "date" && "2023-07-26" : undefined}
// value={"2023-07-26"}
@@ -490,7 +553,7 @@ const FormField = ({
</span>
)}
{helperText && (
<FormHelperText className="web-text-small">{helperText}</FormHelperText>
<FormHelperText color={'gray.500'} className="web-text-small">{helperText}</FormHelperText>
)}
{type === "file" && (
<FormHelperText className="web-text-small">

View File

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

View File

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

View File

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

View File

@@ -26,7 +26,7 @@ const ImageViewer = ({ src, isOpen, onClose }) => {
rounded={6}
w={"100%"}
h={"100%"}
src={"https://tanami.betadelivery.com/" + src}
src={import.meta.env.VITE_IMAGE_URL + src}
/>
</ModalBody>
</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 React from "react";
import './FullscreenLoaders.css'
const FullscreenLoaders = () => {
const FullscreenLoaders = ({height}) => {
return (
<Box
display={"flex"}
@@ -9,16 +10,19 @@ const FullscreenLoaders = () => {
flexDirection={'column'}
alignItems={"center"}
w={"100%"}
h={"100vh"}
h={height ? height: "100vh"}
gap={4}
><Spinner
thickness='3px'
speed='0.65s'
emptyColor='green.100'
color='#004717'
size='lg'
/>
<Text color='#004717' fontSize={'md'} fontWeight={500}>Loading...</Text>
><div className="dot-spinner">
<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 className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
</div>
{/* <Text color='#004717' fontSize={'md'} fontWeight={500}>Loading...</Text> */}
</Box>
);
};

View File

@@ -1,12 +1,18 @@
import React from "react";
import './FullscreenLoaders.css'
const Loader01 = () => {
return (
<div className="lds-ellipsis ">
<div></div>
<div></div>
<div></div>
<div></div>
<div className="dot-spinner">
<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 className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></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 { Select, HStack, Text, Box, IconButton } from '@chakra-ui/react';
import { ChevronLeftIcon, ChevronRightIcon } from '@chakra-ui/icons';
import React, { useState } from "react";
import { Select, HStack, Text, Box, IconButton } from "@chakra-ui/react";
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 totalPages = Math.ceil(totalItems / pageSize);
const handlePageSizeChange = (e) => {
@@ -35,45 +41,54 @@ const Pagination = ({ pageSize, setPageSize, totalItems,isLoading, setCurrentPag
{/* <Text className='web-text-small'>Tanami v0.1</Text> */}
<HStack>
<Select
className="pointer web-text-small"
width={"90px"}
rounded="sm"
size="sm"
value={pageSize}
onChange={handlePageSizeChange}
>
{[ 15, 20, 30]?.map((size) => (
<option key={size} value={size}>
{size}
</option>
))}
</Select>
<Select
className="pointer web-text-small"
width={"90px"}
rounded="sm"
size="sm"
value={pageSize}
onChange={handlePageSizeChange}
>
{[15, 20, 30]?.map((size) => (
<option key={size} value={size}>
{size}
</option>
))}
</Select>
<IconButton
mt={1}
size={'sm'}
rounded="sm"
mt={1}
size={"sm"}
rounded="sm"
icon={<ChevronLeftIcon />}
onClick={paginationPrev}
className="link pointer"
isDisabled={currentPage === 1}
aria-label="Previous Page"
/>
<Text 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
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>
<IconButton
mt={1}
mt={1}
icon={<ChevronRightIcon />}
size={'sm'}
rounded="sm"
size={"sm"}
rounded="sm"
onClick={paginationNext}
className="link pointer"
isDisabled={currentPage === totalPages}
aria-label="Next Page"
/>
</HStack>
</HStack>
);
};
export default Pagination;
export default Pagination;

View File

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

View File

@@ -1,6 +1,7 @@
import { CheckCircleIcon, WarningIcon } from "@chakra-ui/icons";
import { Box, Text } from "@chakra-ui/react";
import React from "react";
import { PiWarningBold } from "react-icons/pi";
const ToastBox = ({ message, status }) => {
return (
@@ -9,10 +10,10 @@ const ToastBox = ({ message, status }) => {
rounded={"sm"}
className="web-text-large d-flex gap-2 align-items-center"
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>
</Box>
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -1,5 +1,83 @@
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) => {
const parts = filePath?.split("/");
@@ -42,3 +120,102 @@ export async function checkEmailValidity(email) {
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 { colorMode, toggleColorMode } = useColorMode();
const [sponser, setSponser] = useState([]);
const [ioStatus, setIoStatus] = useState([]);
const [investors, setInvestors] = useState([
{
@@ -520,7 +519,7 @@ const GlobalStateProvider = ({ children }) => {
rate: 2.66,
},
]);
const [InvestorDetails, setInvestorDetails] = useState([
const [InvestorDetails, setInvestorDetails] = useState([
{
id: 1,
clientId: "SA00000001",
@@ -531,7 +530,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906",
address: "Saudi Arabia",
emailID: "john@gmail.com",
InvestorType:"Retail",
InvestorType: "Retail",
bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456",
@@ -550,7 +549,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906",
address: "Saudi Arabia",
emailID: "john@gmail.com",
InvestorType:"Accredited Investors",
InvestorType: "Accredited Investors",
bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456",
@@ -569,7 +568,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906",
address: "Saudi Arabia",
emailID: "john@gmail.com",
InvestorType:"Retail",
InvestorType: "Retail",
bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456",
@@ -588,7 +587,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906",
address: "Saudi Arabia",
emailID: "john@gmail.com",
InvestorType:"Accredited Investors",
InvestorType: "Accredited Investors",
bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456",
@@ -607,7 +606,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906",
address: "Saudi Arabia",
emailID: "john@gmail.com",
InvestorType:"Retail",
InvestorType: "Retail",
bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456",
@@ -626,7 +625,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906",
address: "Saudi Arabia",
emailID: "john@gmail.com",
InvestorType:"Accredited Investors",
InvestorType: "Accredited Investors",
bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456",
@@ -645,7 +644,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906",
address: "Saudi Arabia",
emailID: "john@gmail.com",
InvestorType:"Retail",
InvestorType: "Retail",
bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456",
@@ -664,7 +663,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906",
address: "Saudi Arabia",
emailID: "john@gmail.com",
InvestorType:"Accredited Investors",
InvestorType: "Accredited Investors",
bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456",
@@ -683,7 +682,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906",
address: "Saudi Arabia",
emailID: "john@gmail.com",
InvestorType:"Retail",
InvestorType: "Retail",
bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456",
@@ -702,7 +701,7 @@ const GlobalStateProvider = ({ children }) => {
phoneNumber: "8940035906",
address: "Saudi Arabia",
emailID: "john@gmail.com",
InvestorType:"Accredited Investors",
InvestorType: "Accredited Investors",
bankName: "Lorem Text",
branchAddress: "Hohenzollernring 58, 95444",
iban: "DE 1234 5678 9012 3456",
@@ -713,136 +712,8 @@ const GlobalStateProvider = ({ children }) => {
},
]);
const [viewInvestor, setViewInvestor] = 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 [viewInvestor, setViewInvestor] = useState([]);
const [transaction, setTransaction] = useState([]);
const [investorTransaction, setInvestorTransaction] = useState([
{
@@ -1091,112 +962,14 @@ const GlobalStateProvider = ({ children }) => {
]);
const [deleteHistory, setDeleteHistory] = useState([
{
id: uuidv4(),
date: "2024-01-15",
Distribution: "Office supplies",
charge: "200.50",
year: "2024",
quarter: "Q1",
amount: 1500,
},
{
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,
id: 1,
firstName: "satyam",
lastName: "Bendal",
clientId: "QA00000003",
RequestedOn: "2024-08-21T08:12:08.000Z",
phoneNumber: "6387524874",
country: "Qatar",
status: "Approved",
},
]);
const [investorRequest, setInvestorRequest] = useState([
@@ -1311,114 +1084,26 @@ const GlobalStateProvider = ({ children }) => {
]);
const [deleteRequest, setDeleteRequest] = useState([
{
id: uuidv4(),
date: getRandomDate(startDate, endDate),
Distribution: "lorem ipsum dummy text",
charge: "500",
year: "2024",
quater: "Q 1",
amount: 1000,
"id": 2,
"firstName": "satyam",
"lastName": "Bendal",
"clientId": "QA00000003",
"RequestedOn": "2024-08-21T09:44:21.000Z",
"phoneNumber": "6387524874",
"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,
},
{
id: uuidv4(),
date: getRandomDate(startDate, endDate),
Distribution: "lorem ipsum dummy text",
charge: "500",
year: "2024",
quater: "Q 1",
amount: 1000,
},
]);
"id": 3,
"firstName": "satyam",
"lastName": "Bendal",
"clientId": "QA00000003",
"RequestedOn": "2024-08-21T09:53:03.000Z",
"phoneNumber": "6387524874",
"country": "Qatar",
"status": "Pending"
}
]);
const [viewIO, setViewIO] = useState([
{
id: 1,
@@ -1450,8 +1135,6 @@ const GlobalStateProvider = ({ children }) => {
},
]);
const [IODetails, setIODetails] = useState({});
const [depositRequest, setDepositRequest] = useState([
{
id: 1,
@@ -1685,6 +1368,28 @@ const GlobalStateProvider = ({ children }) => {
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",
},
{
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([
@@ -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 (
<GlobalStateContext.Provider
value={{
@@ -1795,8 +1634,16 @@ const GlobalStateProvider = ({ children }) => {
setAcademicDocuments,
iOArtifactsTwo,
setIOArtifactsTwo,
ioStatus,
setIoStatus,
InvestorWallet,
setInvestorWallet,
isIOloading,
setIOloading,
users,
setUsers,
fawateerRequest,
setFawateerRequest,
approveHistory,
setApproveHistory
}}
>
{children}

View File

@@ -6,6 +6,8 @@ import logoMiniDark from "../assets/favicon.png";
import { useDispatch } from "react-redux";
import { loginUser } from "../Redux/Slice/auth";
import Button02 from "../Components/Buttons/Button02";
import { CgProfile } from "react-icons/cg";
import {
TbArrowBadgeLeftFilled,
TbListDetails,
@@ -13,7 +15,7 @@ import {
TbTransactionDollar,
} 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 {
Link,
NavLink,
@@ -51,6 +53,8 @@ import {
AlertIcon,
Breadcrumb,
Divider,
Tooltip,
useRadio,
} from "@chakra-ui/react";
import GlobalStateContext from "../Contexts/GlobalStateContext";
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 CutomBreadcrumb 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 userRole = localStorage.getItem("role");
const navigate = useNavigate();
const dispach = useDispatch();
const location = useLocation();
@@ -90,6 +102,18 @@ const DashboardLayout = ({ isOnline }) => {
const [isSplashVisible, setSplashVisible] = useState(true);
const [openIndex, setOpenIndex] = useState(null);
const { data, refetch } = useProfileQuery();
useEffect(() => {
if (
!localStorage.getItem("accessToken") &&
!localStorage.getItem("refreshToken")
) {
logOutHandler();
return navigate("/login");
}
}, []);
useEffect(() => {
const savedIndex = localStorage.getItem("openAccordionIndex");
if (savedIndex !== null) {
@@ -107,8 +131,8 @@ const DashboardLayout = ({ isOnline }) => {
// Set a timer to hide the splash screen after 3 seconds
const timer = setTimeout(() => {
setSplashVisible(false);
},300); // 3000ms = 3 seconds
}, 1000); // 3000ms = 3 seconds
refetch();
// Cleanup the timer
return () => clearTimeout(timer);
}, []);
@@ -117,11 +141,17 @@ const DashboardLayout = ({ isOnline }) => {
setOpenDrawerClick(!openDrawerClick);
};
const logOutHandler = () => {
const [logout] = useLogoutMutation();
const logOutHandler = async () => {
// dispach(loginUser(false));
setIsAuthenticate(false);
Cookies.remove("isAuthenticated");
navigate("/login");
try {
await logout();
localStorage.clear();
navigate("/login");
} catch (error) {}
};
// // Function to get the title based on the route
@@ -132,15 +162,27 @@ const DashboardLayout = ({ isOnline }) => {
case path.startsWith("/sponser"):
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0" /> Sponsorer
<RiMoneyDollarBoxLine className="h4 m-0" /> Sponsor
</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"):
return (
<span className="d-flex align-items-end gap-2">
<VscSymbolClass className="h4 m-0" /> Investment Type
</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"):
return (
<span className="d-flex align-items-end gap-2">
@@ -184,6 +226,37 @@ const DashboardLayout = ({ isOnline }) => {
Investor Transactions
</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"):
return (
<span className="d-flex align-items-end gap-2">
@@ -358,7 +431,7 @@ const DashboardLayout = ({ isOnline }) => {
bottom={4}
right={!slideFromRight ? 4 : "auto"}
left={slideFromRight ? 4 : "auto"}
backgroundColor={"#fff"}
backgroundColor={"#000"}
rounded={"full"}
p={2}
w={8}
@@ -383,11 +456,11 @@ const DashboardLayout = ({ isOnline }) => {
{slideFromRight ? null : (
<aside
className="h-100 position-relative sideBar "
className="h-100 position-relative sideBar"
// onMouseOver={() => setIsDrawerOpen(true)}
// onMouseLeave={() => setIsDrawerOpen(false)}
style={{
width: isDrawerOpen || openDrawerClick ? 232 : 74,
width: isDrawerOpen || openDrawerClick ? 230 : 74,
transition: "width 0.3s ease-in-out", // Smooth transition for width change
// overflow: "hidden",
backgroundColor: "#0041180A",
@@ -400,16 +473,18 @@ const DashboardLayout = ({ isOnline }) => {
isDrawerOpen || openDrawerClick
? "justify-content-start"
: "justify-content-center"
} p-3 pt-3 pb-4 position-relative `}
} p-3 pt-3 pb-3 position-relative `}
height={"10%"}
>
{isDrawerOpen || openDrawerClick ? (
<Image
style={{
width: 120,
width: 110,
}}
src={colorMode === "light" ? logo : logoDark}
alt="Logo"
onClick={() => navigate("/")}
cursor={"pointer"}
/>
) : (
<Image
@@ -418,12 +493,14 @@ const DashboardLayout = ({ isOnline }) => {
}}
src={colorMode === "light" ? logoMini : logoMiniDark}
alt="Logo"
onClick={() => navigate("/")}
cursor={"pointer"}
/>
)}
</div>
<Box
className="ps-2 scroll-bar pe-1"
className="ps-2 scroll-bar pe-1 pt-3"
style={{
height: "90%",
overflowY: "scroll",
@@ -442,40 +519,53 @@ const DashboardLayout = ({ isOnline }) => {
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`}
<Tooltip
isDisabled={isDrawerOpen || openDrawerClick}
hasArrow
bg={"#fff"}
fontSize={"xs"}
label={title}
placement="top-start"
color={"blue.800"}
>
<Box
as="span"
display={"flex"}
gap={2}
alignItems={"center"}
<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`}
>
{/* {Icon && title === "Admin" ? <Image w={15} src={shield} /> : <Icon className={`web-text-large`} />} */}
{Icon && (
<Icon
fontSize={title === "Admin" ? "18px" : "15px"}
/>
)}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick ? "flex" : "none"
}
alignItems="center"
overflow="hidden"
textAlign={"left"}
<Box
as="span"
display={"flex"}
gap={2}
alignItems={"center"}
>
{title}
</Text>
</Box>
<AccordionIcon />
</AccordionButton>
{/* {Icon && title === "Admin" ? <Image w={15} src={shield} /> : <Icon className={`web-text-large`} />} */}
{Icon && (
<Icon
fontSize={title === "Admin" ? "18px" : "15px"}
/>
)}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick
? "flex"
: "none"
}
alignItems="center"
overflow="hidden"
textAlign={"left"}
>
{title}
</Text>
</Box>
<AccordionIcon />
</AccordionButton>
</Tooltip>
<AccordionPanel
p={0}
pb={1}
@@ -488,65 +578,81 @@ const DashboardLayout = ({ isOnline }) => {
{ title: subMenuTitle, path: link, icon: SubIcon },
i
) => (
<Box
key={i}
style={{ height: "auto", position: "relative" }}
className={`${
isDrawerOpen || openDrawerClick
? " web-text-medium ps-4"
: " web-text-xlarge justify-content-center"
} d-flex align-items-center p-0`}
<Tooltip
isDisabled={isDrawerOpen || openDrawerClick}
hasArrow
bg={"#fff"}
fontSize={"xs"}
label={subMenuTitle}
placement="right"
color={"blue.800"}
>
<Box
backgroundColor={"gray.300"}
style={{
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
key={i}
style={{ height: "auto", position: "relative" }}
className={`${
isDrawerOpen || openDrawerClick
? "p-2 ps-1 ms-2 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 `}
to={link}
? " web-text-medium ps-4"
: " web-text-xlarge justify-content-center"
} d-flex align-items-center p-0`}
>
{SubIcon && (
<SubIcon
className="web-text-large ms-2"
style={{ zIndex: 111 }}
/>
)}
<Text
as={"span"}
display={
<Box
backgroundColor={"gray.300"}
style={{
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={`${
isDrawerOpen || openDrawerClick
? "flex"
: "none"
}
alignItems="center"
overflow="hidden"
? "p-2 ps-1 ms-2 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 `}
to={link}
>
{subMenuTitle}
</Text>
</NavLink>
</Box>
{SubIcon && (
<SubIcon
className="web-text-large ms-2"
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>
@@ -564,28 +670,37 @@ const DashboardLayout = ({ isOnline }) => {
);
} else if (type === "single") {
return (
<NavLink
key={index}
style={{ height: "auto", position: "relative" }}
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}
<Tooltip
hasArrow
bg={"#fff"}
fontSize={"xs"}
label={title}
placement="top-start"
color={"blue.800"}
>
{Icon && <Icon className="web-text-large ms-2" />}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick ? "flex" : "none"
}
alignItems="center"
overflow="hidden"
<NavLink
key={index}
style={{ height: "auto", position: "relative" }}
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}
>
{title}
</Text>
</NavLink>
{Icon && <Icon className="web-text-large ms-2" />}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick ? "flex" : "none"
}
alignItems="center"
overflow="hidden"
>
{title}
</Text>
</NavLink>
</Tooltip>
);
} else {
return null;
@@ -615,21 +730,17 @@ const DashboardLayout = ({ isOnline }) => {
<ArrowRightIcon className="web-text-small " />
)}
</Button>
<Box
id="google_translate_element"
display="block"
className="bg-danger"
/>
{/* <Text textAlign={'center'} fontWeight={500} fontSize={'xs'} color={"gray.600"}>{getCountdownTimer(localStorage.getItem('accessTokenExp'))}</Text> */}
</aside>
)}
<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={{
width: `calc(100% - ${isDrawerOpen || openDrawerClick ? 232 : 74}px)`,
width: `calc(100% - ${isDrawerOpen || openDrawerClick ? 230 : 74}px)`,
transition: "width 0.3s ease-in-out",
}}
>
{/* <header className="p-2 ps-0 pt-3 fw-400 border-bottom">
@@ -637,261 +748,46 @@ const DashboardLayout = ({ isOnline }) => {
</header> */}
<HeaderMain
data={data}
slideDirecttion={slideFromRight}
logOutHandler={logOutHandler}
icon
title={getTitle()}
/>
{/* <CustomBreadcrumb /> */}
<AppContent />
{/* <CustomBreadcrumb /> */}
<AppContent data={data} />
</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>
);
};
export default DashboardLayout;
const AppContent = () => {
const AppContent = ({ data }) => {
return (
<Routes>
{RouteLink.map(({ path, Component }, index) => (
<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 />} />
</Routes>
);

View File

@@ -11,66 +11,84 @@ import {
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
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 GlobalStateContext from "../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../Components/CustomAlertDialog";
import { formatDate } from "../../Components/Functions/UTCConvertor";
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";
const DeletionHistory = () => {
const toast = useToast();
const { slideFromRight, deleteHistory, setDeleteHistory } =
const { slideFromRight, setDeleteHistory } =
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 Filter]================================================================
const filteredData = deleteHistory.filter((item) => {
// Filter by name (case insensitive)
const name = item.Distribution;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
// =========================== [Use State] =============================
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const [searchTerm, setSearchTerm] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
// Filter by status
// const status = item.status;
// const statusLower = status ? "active" : "inactive";
// 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 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]================================================================
const tableHeadRow = [
"Sr No.",
"Date",
"Distribution Amount",
"Charges (USD)",
"Year",
"Quater",
"Action",
"Request On",
"Client ID",
"First Name",
"Last Name",
"Country",
"Phone Number",
"Status"
];
const extractedArray = filteredData?.map((item, index) => ({
const extractedArray = deleteHistory?.data?.rows?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Text
@@ -83,7 +101,7 @@ const DeletionHistory = () => {
{index + 1}.
</Text>
),
"Date": (
"Request On": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
@@ -91,10 +109,10 @@ const DeletionHistory = () => {
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
>
{formatDate(item.date)}
{formatDate(item.Requested_on)}
</Text>
),
"Distribution Amount": (
"Client ID": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
@@ -102,10 +120,10 @@ const DeletionHistory = () => {
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
>
{item.Distribution}
{item.clientId}
</Text>
),
"Charges (USD)": (
"First Name": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
@@ -113,11 +131,11 @@ const DeletionHistory = () => {
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
>
{item.charge}
{item.firstName}
{/* {formatDate(item.charge)} */}
</Text>
),
Year: (
"Last Name": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
@@ -125,10 +143,10 @@ const DeletionHistory = () => {
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
>
{item.year}
{item.lastName}
</Text>
),
Quater: (
"Country": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
@@ -136,18 +154,31 @@ const DeletionHistory = () => {
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
>
{item.quarter}
{item.country}
</Text>
),
Action: (
<Box display={'flex'} justifyContent={'space-around'}>
<Tooltip rounded={'sm'} fontSize={'xs'} label='Accept' bg='#fff' color={'green.500'} placement="left-start">
<Button color="green.500" rounded={'sm'} size={'xs'}>
<CheckIcon /></Button></Tooltip>
<Tooltip rounded={'sm'} fontSize={'xs'} label='Reject' bg='#fff' color={'red.500'} placement="left-start">
<Button color="red.500" rounded={'sm'} size={'xs'}>
<CloseIcon /></Button></Tooltip>
</Box>
"Phone Number": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
>
{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)}
/>
<HStack display={"flex"} alignItems={"center"}>
{/* <HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} />
</HStack>
</HStack> */}
</HStack>
</Box>
<DataTable
<NormalTable
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}

View File

@@ -6,15 +6,20 @@ import {
HStack,
Input,
Text,
Tooltip,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
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 GlobalStateContext from "../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../Components/CustomAlertDialog";
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";
const DeletionRequest = () => {
@@ -28,6 +33,32 @@ const DeletionRequest = () => {
const [mouseEntered, setMouseEntered] = useState(false);
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(() => {
// Simulate loading
const timer = setTimeout(() => {
@@ -39,9 +70,9 @@ const DeletionRequest = () => {
}, []);
// ====================================================[Table Filter]================================================================
const filteredData = deleteRequest.filter((item) => {
const filteredData = data?.data?.rows?.filter((item) => {
// Filter by name (case insensitive)
const name = item.Distribution;
const name = item?.firstName;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
@@ -60,19 +91,21 @@ const DeletionRequest = () => {
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr No.",
"Date",
"Distribution Amount",
"Charges (USD)",
"Year",
"Quater",
"Amount",
"Requested on",
"Client ID",
"First name",
"Last name",
"Country",
"Phone number",
"Status",
"Action"
];
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
justifyContent={"left"}
as={"span"}
color={"gray.800"}
className="d-flex align-items-center web-text-small"
@@ -81,74 +114,112 @@ const DeletionRequest = () => {
{index + 1}.
</Text>
),
"Date": (
"Requested on": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
justifyContent={"left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
>
{formatDate(item.date)}
{formatDate(item?.Requested_on)}
</Text>
),
"Distribution Amount": (
"Client ID": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
justifyContent={"left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center web-text-small"
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
>
{item.Distribution}
{item?.clientId}
</Text>
),
"Charges (USD)": (
"First name": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
justifyContent={"left"}
as={"span"}
color={"gray.800"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
>
{item.charge}
{item?.firstName}
{/* {formatDate(item.charge)} */}
</Text>
),
Year: (
"Last name": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
justifyContent={"left"}
as={"span"}
color={"gray.800"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
>
{item.year}
{item?.lastName}
</Text>
),
Quater: (
Country: (
<Text
justifyContent={slideFromRight ? "right" : "left"}
justifyContent={"left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
>
{item.quater}
{item?.country}
</Text>
),
Amount: (
"Phone number": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
justifyContent={"left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
>
{item.amount}
{item?.phoneNumber}
</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 = () => {
@@ -187,17 +258,17 @@ const DeletionRequest = () => {
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
{/* <HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} />
</HStack>
</HStack> */}
</HStack>
</Box>
<DataTable
<NormalTable
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
isLoading={DeletionLoading}
viewActionId={actionId}
setViewActionId={setActionId}
// totalPages={10}
@@ -213,6 +284,22 @@ const DeletionRequest = () => {
alertHandler={handleDelete}
isLoading={isLoading}
/>
<DeletionRequestApprove
data={deleteRequest}
isOpen={isConfirmOpen}
onClose={onConfirmClose}
id={actionId}
// firstField={firstField}
/>
{/* <DepositRequestReject
isOpen={isRejectOpen}
onClose={onRejectClose}
id={actionId}
/> */}
</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,
HStack,
Input,
Menu,
MenuButton,
MenuItem,
MenuList,
Portal,
Select,
Switch,
Tag,
Text,
Tooltip,
useDisclosure,
@@ -22,11 +14,7 @@ import React, { useContext, useEffect, useState, useRef } from "react";
import { HiDotsVertical } from "react-icons/hi";
import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
import {
AddIcon,
DeleteIcon,
EditIcon,
EmailIcon,
ViewIcon,
} from "@chakra-ui/icons";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import DataTable from "../../../Components/DataTable/DataTable";
@@ -36,6 +24,7 @@ import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import ToastBox from "../../../Components/ToastBox";
import { debounce } from "../../Master/Sponser/AddSponser";
import { useGetBankQuery } from "../../../Services/bank.details.service";
import NormalTable from '../../../Components/DataTable/NormalTable'
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
@@ -64,7 +53,6 @@ const BankDetails = () => {
error,
} = useGetBankQuery({ page: 1, size: 10 });
console.log(bankDetails?.data);
useEffect(() => {
// Simulate loading
@@ -115,10 +103,9 @@ const BankDetails = () => {
return nameMatches;
});
console.log(bankDetails);
const extractedArray = filteredData?.map((item) => ({
const extractedArray = filteredData?.map((item,index) => ({
id: item?.id,
"Sr N/O": (
<Text
@@ -133,18 +120,18 @@ const BankDetails = () => {
"Country name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.country_xid}
{item?.country?.countryName}
</Text>
</Box>
),
"Account Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.accountName}
{item?.accountName}
</Text>
</Box>
),
"Account No ": (
"Account No": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.accountNumber}
@@ -159,16 +146,17 @@ const BankDetails = () => {
</Box>
),
Action: (
<Box display={"flex"} justifyContent={"space-between"} gap={2}>
<Box display={"flex"} justifyContent={"space-between"}>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="View"
label="Edit"
bg="#fff"
color={"green.500"}
placement="top"
>
<Button
bg={index % 2 === 0 ? "#6311cb14" : "#fff"}
onClick={() => {
navigate(`/bank-details/edit-bank-details/${item.id}`);
}}
@@ -229,8 +217,8 @@ const BankDetails = () => {
</HStack>
</Box>
<DataTable
emptyMessage={`We don't have any Sponers `}
<NormalTable
emptyMessage={`We don't have any Details`}
tableHeadRow={tableHeadRow}
data={extractedArray}
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 { Box, useToast } from "@chakra-ui/react";
import { useForm } from "react-hook-form";
@@ -10,7 +10,8 @@ import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import SwitchButton from "../../../Components/SwitchButton";
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";
const editBankSchema = yup.object().shape({
@@ -28,12 +29,17 @@ const EditBankDetails = () => {
const id = params?.id;
const [isLoadingBtn, setIsLoadingBtn] = useState(false);
const { InvestorDetails, setInvestorDetails, slideFromRight } =
useContext(GlobalStateContext);
const [alert, setAlert] = useState(false);
const [form, setForm] = useState({});
const [isSwitchOn, setIsSwitchOn] = useState(false);
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 {
control,
@@ -45,16 +51,22 @@ const EditBankDetails = () => {
});
useEffect(() => {
if (bankDetails) {
setInvestorDetails({
...bankDetails?.data,
});
// refetch()
if (bankDetails?.data) {
reset({
accountNumber: bankDetails.accountNumber,
swiftCode: bankDetails.swiftCode,
bankName: bankDetails.bankName,
bankAddress: bankDetails.bankAddress,
IBANnumber: bankDetails.IBANnumber,
accountName: bankDetails?.data?.accountName,
countryName: bankDetails?.data?.countryName,
accountNumber: bankDetails?.data?.accountNumber,
swiftCode: bankDetails?.data?.swiftCode,
bankName: bankDetails?.data?.bankName,
bankAddress: bankDetails?.data?.bankAddress,
IBANnumber: bankDetails?.data?.IBANnumber,
});
}
}, [bankDetails, reset]);
}, [bankDetails, reset,id]);
if (isLoading) {
return <FullscreenLoaders />;
@@ -62,7 +74,6 @@ const EditBankDetails = () => {
const handleConfirm = async () => {
setIsLoadingBtn(true);
try {
const formData = {
...form,
@@ -77,6 +88,7 @@ const EditBankDetails = () => {
setIsLoadingBtn(false);
setAlert(false);
refetch()
navigate("/bank-details");
} else {
throw new Error("Something went wrong");
@@ -91,66 +103,76 @@ const EditBankDetails = () => {
}
};
console.log(bankDetails?.accountNumber);
const formEditFields = [
{
label: "Country Name",
placeHolder: "Enter country name",
name: "countryName",
type: "text",
isRequired: true,
section: "Add Details",
maxLength: 50,
width: "32%",
},
// {
// label: "Country Name",
// placeHolder:"",
// name: "countryName",
// type: "text",
// isRequired: true,
// section: "Add Details",
// maxLength: 50,
// width: "32%",
// },
{
label: "Account Name",
// value:bankDetails?.accountName,
name: "accountName",
placeHolder: "Enter account name",
placeHolder: "",
type: "text",
isRequired: true,
section: "Add Details",
maxLength: 255,
// maxLength: 255,
width: "32%",
},
{
label: "Account Number *",
label: "Account Number",
name: "accountNumber",
placeHolder: "Enter account number",
placeHolder: "",
type: "text",
section: "Add Details",
width: "32%",
isRequired: true,
},
{
label: "IBAN Number",
name: "IBANnumber",
placeHolder: "Enter IBAN number",
placeHolder: "",
type: "text",
section: "Add Details",
width: "32%",
isRequired: true,
},
{
label: "SWIFT Code",
value: bankDetails?.data?.swiftCode,
name: "swiftCode",
placeHolder: "Enter SWIFT code",
placeHolder: "",
type: "text",
section: "Add Details",
width: "32%",
isRequired: true,
},
{
label: "Bank Name",
name: "bankName",
placeHolder: "Enter bank name",
placeHolder: "",
type: "text",
section: "Add Details",
width: "32%",
isRequired: true,
},
{
label: "Bank Address",
name: "bankAddress",
placeHolder: "Enter bank address",
placeHolder: "",
type: "text",
section: "Add Details",
width: "32%",
isRequired: true,
},
];
@@ -163,7 +185,7 @@ const EditBankDetails = () => {
return groups;
}, {});
const onSubmit = (data) => {
const onSubmit = async(data) => {
if (!Object.keys(errors).length) {
setForm(data);
setAlert(true);

View File

@@ -10,6 +10,7 @@ import {
Textarea,
Button,
Text,
useToast,
} from "@chakra-ui/react";
import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
@@ -19,13 +20,35 @@ import { v4 as uuidv4 } from "uuid";
import GlobalStateContext from "../../Contexts/GlobalStateContext";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
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 ToastBox from "../../Components/ToastBox";
export const addSponser = yup.object().shape({
phoneNumber: yup.string().required("Phone Number is required"),
emailAddress: yup.string().required("E-mail ID is required"),
websiteUrl: yup.string().required("Website URL is required"),
phoneNumber: yup
.string()
.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) {
@@ -37,8 +60,10 @@ export function debounce(func, delay) {
}
const Contact = () => {
const toast = useToast();
const navigate = useNavigate();
const [form, setForm] = useState({});
const [isLoading, setIsLoading] = useState(false);
// const { sponser, setSponser } = useContext(GlobalStateContext);
const {
@@ -48,24 +73,24 @@ const Contact = () => {
formState: { errors },
} = useForm({
resolver: yupResolver(addSponser),
// mode: all
});
console.log(errors);
const {
data: contact,
isLoading: contactLoading,
error,
} = useGetContactQuery({ page: 1, size: 10 });
console.log(contact?.data);
} = useGetContactQuery();
const [updateContact] = useUpdateContactMutation();
useEffect(() => {
if (contact) {
reset({
phoneNumber: contact.phoneNumber,
emailAddress: contact.emailAddress,
websiteUrl: contact.websiteUrl,
phoneNumber: contact?.data[0]?.phoneNumber,
emailAddress: contact?.data[0]?.emailAddress,
websiteUrl: contact?.data[0]?.websiteUrl,
});
}
}, [contact, reset]);
@@ -81,8 +106,8 @@ const Contact = () => {
name: "phoneNumber",
type: "text",
isRequired: true,
section: "Add Details",
defaultValue: contact?.phoneNumber || "",
section:"",
// value: contact?.phoneNumber || "",
},
{
label: "E-mail ID",
@@ -90,8 +115,8 @@ const Contact = () => {
placeHolder: " ",
type: "text",
isRequired: true,
section: "Add Details",
defaultValue: contact?.emailAddress || "",
section:"",
// value: contact?.emailAddress || "",
},
{
label: "Website URL",
@@ -99,8 +124,8 @@ const Contact = () => {
placeHolder: " ",
type: "text",
isRequired: true,
section: "Add Details",
defaultValue: contact?.websiteUrl || "",
section:"",
// value: contact?.websiteUrl || "",
},
];
@@ -113,10 +138,20 @@ const Contact = () => {
return groups;
}, {});
const onSubmit = (data) => {
if (!Object.keys(errors).length) {
setForm(data);
setAlert(true);
const onSubmit = async (data) => {
setIsLoading(true);
try {
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}
errors={errors}
onSubmit={handleSubmit(onSubmit)}
btnLoading={isLoading}
/>
</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 error from "../assets/Error.svg"
import robot from "../../assets/robot.png"
// import robot from "../assets/robot.png"
const Notification = () => {
return (
<Box
h={'100vh'}
display={'flex'}
justifyContent={'center'}
alignItems={'center'}
flexDirection={'column'}
gap={8}
>
<Image src={robot} w={"171px"} />
{/* <Text color={'green.800'} as={'span'} fontSize={'small'}>The requested URL was not found on this server.</Text> */}
</Box>
)
}
import React, { useContext, useEffect, useState } from "react";
import {
Badge,
Box,
Button,
Text,
Tooltip,
useToast,
} from "@chakra-ui/react";
import { useForm} from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import { useNavigate } from "react-router-dom";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import FormInputMain from "../../Components/FormInputMain";
import {
useGetContactQuery,
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 error from "../assets/Error.svg"
import robot from "../../assets/robot.png"
// import robot from "../assets/robot.png"
const Users = () => {
return (
<Box
h={'100vh'}
display={'flex'}
justifyContent={'center'}
alignItems={'center'}
flexDirection={'column'}
gap={8}
>
<Image src={robot} w={"171px"} />
{/* <Text color={'green.800'} as={'span'} fontSize={'small'}>The requested URL was not found on this server.</Text> */}
</Box>
)
}
import {
Box,
Button,
HStack,
Input,
Text,
Tooltip,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState, useRef } from "react";
import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
import {EditIcon,} from "@chakra-ui/icons";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import DataTable from "../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../Components/CustomAlertDialog";
import ToastBox from "../../Components/ToastBox";
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 DepositRequestApprove from "./DepositRequestApprove";
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 navigate = useNavigate();
const toast = useToast();
const { depositRequest, setDepositRequest, slideFromRight } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [actionId, setActionId] = useState("");
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const {
@@ -55,31 +62,63 @@ const DepositRequest = () => {
onOpen: onRejectOpen,
onClose: onRejectClose,
} = 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(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
const handler = setTimeout(() => {
setDebouncedSearchTerm(searchTerm);
}, 500); // Adjust delay as needed
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]================================================================
const tableHeadRow = [
"Sr.no",
"Date",
"Client ID",
"First Name",
"Last Name",
"Country",
"Phone Number",
"Currency",
"Deposit Amount",
"Fees",
"Total Amount",
"Amount in Investor currency",
"Deposit Date",
"Action",
];
@@ -96,179 +135,172 @@ const DepositRequest = () => {
});
}, 300);
// ====================================================[Table Filter]================================================================
const filteredData = depositRequest.filter((item) => {
// Filter by name (case insensitive)
const name = item.clientId;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
const filteredData = data?.data?.rows
.filter((item) => {
// Filter by name (case insensitive)
const name = [item.firstName, item.lastName, item.countryName]
.filter(Boolean)
.join(" ");
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
// Filter by status
// const status = item.status;
// const statusLower = status ? "active" : "inactive";
// 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);
// const statusMatches =
// statusFilter === "all" ||
// (statusFilter === "active" && status === true) ||
// (statusFilter === "inactive" && status === false);
return nameMatches;
});
return nameMatches;
})
.sort((b, a) => new Date(a.createdAt) - new Date(b.createdAt));
const [extractedArray, setExtractedArray] = useState(
filteredData?.map((item, index) => ({
// id: item?.id,
"Sr.no": (
<Text
w={"30px"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{index + 1}
const extractedArray = data?.data?.rows?.map((item, idx) => ({
// id: item?.id,
"Sr.no": (
<Text
w={"20px"}
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}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.firstName}
</Text>
),
Date: (
<Text
w={"60px"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.date}
</Box>
),
"Last Name": (
<Box w={"70px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.lastName}
</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.clientId}
</Box>
),
Country: (
<Box w={"100px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.countryName}
</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>
),
Action: (
<Box display={"flex"} justifyContent={"space-around"} gap={2}>
<Tooltip
</Box>
),
"Phone Number": (
<Box w={"80px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.mobileNumber}
</Text>
</Box>
),
"Deposit Amount": (
<Box
display={"flex"}
justifyContent={"end"}
w={"130px"}
isTruncated={true}
>
<Text as={"span"} color={"teal.900"}>
{/* {formatCurrency(removeTrailingZeros(item?.investorAmount))} */}
{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>
),
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"}
fontSize={"xs"}
label="Approve"
bg="#fff"
color={"green.500"}
placement="left-start"
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={2}
py={1}
onClick={() => {
setActionId(item.id);
onConfirmOpen();
}}
colorScheme="green"
variant={"solid"}
cursor={"pointer"}
>
<Badge
colorScheme="forestGreen"
color="green.500"
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={2}
py={1}
onClick={onConfirmOpen}
>
<CheckIcon fontSize={"12px"} />
</Badge>
</Tooltip>
<Tooltip
<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"}
fontSize={"xs"}
label="Reject"
bg="#fff"
color={"red.500"}
placement="left-start"
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={2}
onClick={() => {
setActionId(item.id);
onRejectOpen();
}}
py={1}
// variant={"solid"}
>
<Badge
colorScheme="red"
color="red.500"
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={2}
onClick={onRejectOpen}
py={1}
>
<CloseIcon fontSize={"10px"} />
</Badge>
</Tooltip>
</Box>
),
}))
);
<CloseIcon fontSize={"10px"} />
</Button>
</Tooltip>
</Box>
),
}));
const handleDelete = () => {
const IOtype = investmentType.filter(
@@ -307,17 +339,23 @@ const DepositRequest = () => {
/>
<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>
</Box>
<DataTable
<NormalTable
emptyMessage={`We don't have any Investment type `}
tableHeadRow={tableHeadRow}
setData={setExtractedArray}
data={extractedArray}
isLoading={isLoading}
isLoading={depositRequestLoading}
viewActionId={actionId}
setViewActionId={setActionId}
// totalPages={10}
@@ -334,11 +372,17 @@ const DepositRequest = () => {
isLoading={isLoading}
/>
<DepositRequestApprove
data={data?.data?.rows}
isOpen={isConfirmOpen}
onClose={onConfirmClose}
id={actionId}
// firstField={firstField}
/>
<DepositRequestReject isOpen={isRejectOpen} onClose={onRejectClose} />
<DepositRequestReject
isOpen={isRejectOpen}
onClose={onRejectClose}
id={actionId}
/>
</Box>
);
};

View File

@@ -1,128 +1,242 @@
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"),
Badge,
Box,
Button,
FormControl,
FormErrorMessage,
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 { 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 }) => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const onSubmit = (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 />
console.log(data?.data?.investorAmount);
const {
control,
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
useEffect(() => {
if (data) {
const investorAmount = parseFloat(data?.data?.investorAmount);
reset({
investorAmount: investorAmount,
accountName: data?.data?.accountName,
});
}
}, [id, data, reset]);
const onSubmit = async (data) => {
setIsBtnLoading(true);
const formData = new FormData();
formData.append("investorAmount", data.investorAmount);
formData.append("comment", data.comment);
const file = data.supporting_FileName["0"];
formData.append("supporting_FileName", file);
try {
const res = await updateDepositRequest({ id, data: formData });
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)}>
<ModalBody>
<FormControl mb={4}>
<FormLabel fontSize="sm">Deposit Amount</FormLabel>
{/* <FormControl mb={4} isRequired>
<FormLabel fontSize="sm">
Deposit Amount
<Badge colorScheme="green">{data?.data?.currencyCode}</Badge>
</FormLabel>
<Input
focusBorderColor='green.400'
name="fileName"
{...register("fileName")}
focusBorderColor="green.400"
name="investorAmount"
{...register("investorAmount")}
value={formatCurrency(watch("investorAmount"))}
fontSize="sm"
type="text"
type="number"
size="sm"
placeholder={"$100,000"}
readOnly
placeholder={"100,000"}
textAlign={"right"}
// readOnly
/>
</FormControl>
<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 && (
{errors.investorAmount && (
<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>
)}
</FormControl>
<FormControl mb={4}>
<FormLabel fontSize="sm">Total Amount</FormLabel>
<Input
focusBorderColor='green.400'
name="fileName"
{...register("fileName")}
<FormLabel fontSize="sm">Comments</FormLabel>
<Textarea
rows={5}
focusBorderColor="green.400"
name="comment"
{...register("comment")}
fontSize="sm"
type="text"
type="textarea"
size="sm"
placeholder={"$100,000"}
placeholder={"Enter your comments...."}
resize={"none"}
/>
{errors.totalAmount && (
{errors.comment && (
<Text fontSize="xs" color="red">
{errors.totalAmount.message}
{errors.comment.message}
</Text>
)}
</FormControl>
</ModalBody>
<ModalFooter>
<Button colorScheme="gray" mr={3} onClick={onClose} size={'sm'} rounded={'sm'}>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel
</Button>
<Button colorScheme="forestGreen" variant="solid" size={'sm'} rounded={'sm'}>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Confirm
</Button>
</ModalFooter>
</Box>
</ModalContent>
</Modal>
);
};
export default DepositRequestApprove;
)}
</ModalContent>
</Modal>
);
};
export default DepositRequestApprove;

View File

@@ -1,99 +1,163 @@
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"),
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 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 {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const onSubmit = async(data) => {
setIsBtnLoading(true)
try {
const res = await depositReject({ id ,data})
const onSubmit = (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>
);
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);
}
};
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,
Tooltip,
useToast,
useDisclosure
useDisclosure,
Link,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
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 { OPACITY_ON_LOAD } from "../../../Layout/animations";
import Pagination from "../../../Components/Pagination";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import ToastBox from "../../../Components/ToastBox";
import DataTable from "../../../Components/DataTable/DataTable";
import NormalTable from "../../../Components/DataTable/NormalTable";
import ConfirmModal from "./ConfirmModal";
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 navigate = useNavigate();
const toast = useToast();
const { depositHistory, setDepositHistory, 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: isConfirmOpen,
onOpen: onConfirmOpen,
onClose: onConfirmClose,
} = useDisclosure();
const {
isOpen: isRejectOpen,
onOpen: onRejectOpen,
onClose: onRejectClose,
} = useDisclosure();
// const {
// isOpen: isConfirmOpen,
// onOpen: onConfirmOpen,
// onClose: onConfirmClose,
// } = useDisclosure();
// const {
// isOpen: isRejectOpen,
// onOpen: onRejectOpen,
// onClose: onRejectClose,
// } = 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(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
const handler = setTimeout(() => {
setDebouncedSearchTerm(searchTerm);
}, 500); // Adjust delay as needed
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]================================================================
const tableHeadRow = [
"Sr.no",
"Date",
"Client ID",
"First Name",
"Last Name",
"Country",
"Phone Number",
"Currency",
"Deposit Amount",
"Fees",
"Total Amount",
"Amount in Investor currency",
"Deposit Date",
"Status",
"Supporting's",
];
const handleUpdateStatus = debounce((id) => {
@@ -87,36 +124,130 @@ const DepositHistory = () => {
});
}, 300);
// ====================================================[Table Filter]================================================================
const filteredData = depositHistory.filter((item) => {
// Filter by name (case insensitive)
const name = item.clientId;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
const filteredData = data?.data?.rows
.filter((item) => {
// Filter by name (case insensitive)
const name = [item.firstName, item.lastName, item.countryName]
.filter(Boolean)
.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) => {
// setActionId(id);
// onViewOpen();
// };
const [extractedArray, setExtractedArray] = useState(
filteredData?.map((item, index) => ({
"Sr.no": (
<Text
w={"30px"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{index + 1}
const extractedArray = data?.data?.rows?.map((item, idx) => ({
"Sr.no": (
<Text
w={"10px"}
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={"60px"}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.firstName}
</Text>
),
"Date": (
</Box>
),
"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
w={"60px"}
justifyContent={slideFromRight ? "right" : "left"}
@@ -125,110 +256,34 @@ const DepositHistory = () => {
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.date}
</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.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?.supporting_FileName} */}
<Badge
px={2}
py={0.5}
display={"flex"}
alignItems={"center"}
textTransform={"inherit"}
fontWeight={500}
colorScheme={"forestGreen"}
>
{item.status}
</Text>
</Box>
<Link
href={import.meta.env.VITE_IMAGE_URL + item?.supporting_FileName}
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 IOtype = investmentType.filter(
@@ -245,15 +300,14 @@ const DepositHistory = () => {
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<ConfirmModal
{/* <ConfirmModal
isOpen={isConfirmOpen}
onClose={onConfirmClose}
// firstField={firstField}
/>
<RejectModal
isOpen={isRejectOpen}
onClose={onRejectClose}
/>
/> */}
<Box bg="white.500">
<HStack
display={"flex"}
@@ -276,17 +330,24 @@ const DepositHistory = () => {
/>
<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>
</Box>
<DataTable
<NormalTable
emptyMessage={`We don't have any Investment type `}
tableHeadRow={tableHeadRow}
setData={setExtractedArray}
// setData={setExtractedArray}
data={extractedArray}
isLoading={isLoading}
isLoading={depositHistoryLoading}
viewActionId={actionId}
setViewActionId={setActionId}
setMouseEnteredId={setMouseEnteredId}

View File

@@ -46,7 +46,7 @@ const RejectModal = ({ isOpen, onClose, firstField }) => {
setCreate((prevCreate) => [...prevCreate, newDocument]);
onClose();
};
};
const handleFileChange = (event) => {
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,
useToast,
} 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 { Link, Link as RouterLink,useNavigate } from "react-router-dom";
import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
import {
AddIcon,
DeleteIcon,
@@ -28,30 +30,68 @@ import {
EmailIcon,
ViewIcon,
} 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 GlobalStateContext from "../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../Components/CustomAlertDialog";
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 toast = useToast();
const thirdField = useRef();
const { bankInvestor, setBankInvestor, slideFromRight } =
const { InvestorDetails, setInvestorDetails, 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 {
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(() => {
// Simulate loading
@@ -65,21 +105,22 @@ const BankInvestor = () => {
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr N/O",
"Sr No",
"Client ID",
"First Name",
"Last Name",
"Country",
"Country",
"Phone Number",
"E-mail ID",
"KYC Status",
"Action",
// "Type",
// "KYC Status",
"Approval Status",
];
const handleUpdateStatus = debounce((id) => {
setBankInvestor((prevData) =>
prevData.map((bankInvestor) =>
bankInvestor.id === id ? { ...bankInvestor } : bankInvestor
setInvestorDetails((prevData) =>
prevData.map((InvestorDetails) =>
InvestorDetails.id === id ? { ...InvestorDetails } : InvestorDetails
)
);
toast({
@@ -88,9 +129,9 @@ const BankInvestor = () => {
}, 300);
// ====================================================[Table Filter]================================================================
const filteredData = bankInvestor?.filter((item) => {
const filteredData = investorDetails?.data?.rows?.filter((item) => {
// 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 nameMatches = name?.toLowerCase().includes(searchLower);
@@ -106,74 +147,100 @@ const BankInvestor = () => {
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,
"Sr N/O": (
"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}
{/* {item.id} */}
{generateSerialNumber(idx,currentPage, pageSize )}
</Text>
),
"Client ID": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.clientId}
{item.clientReference_id}
</Text>
</Box>
</Box>
),
"First Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.firstName}
{item?.principal?.firstName}
</Text>
</Box>
),
"Last Name": (
<Box w={"auto"} isTruncated={true}>
<Box w={"70px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.lastName}
{item?.principal?.lastName}
</Text>
</Box>
),
Country: (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.country}
{item?.country?.countryName}
</Text>
</Box>
),
"Phone Number": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.phoneNumber}
{item?.principal?.mobileNumber}
</Text>
</Box>
),
"E-mail ID": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.mailId}
{item?.principal?.emailAddress}
</Text>
</Box>
),
"KYC Status": (
"Type": (
<Box w={"auto"} isTruncated={true}>
<Badge fontWeight={'500'} textTransform={'none'} color={item.status === "Completed" ? "blue" : "red"} px={2} py={0.5}>
{item.status}
</Badge>
<Text as={"span"} >
<Badge color={"forestGreen.500"} variant={'ghost'} fontWeight={"700"} px={2} py={0.5}>
{item?.investor_type?.investorTypeName}
</Badge>
</Text>
</Box>
),
Action: (
<Switch
size={"sm"}
colorScheme="forestGreen"
onChange={() => handleUpdateStatus(item.id)}
isChecked={item.status}
/>
"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>
),
}));
@@ -195,6 +262,10 @@ const BankInvestor = () => {
onEditOpen();
};
console.log(investorDetails?.data?.totalItems);
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<Box bg="white.500">
@@ -208,7 +279,7 @@ const BankInvestor = () => {
spacing="24px"
>
<Input
mt={1}
mt={1}
type="search"
width={300}
placeholder="Search..."
@@ -218,17 +289,45 @@ const BankInvestor = () => {
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<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>
</Box>
<DataTable
<NormalTable
// centered={true}
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
isLoading={investorDetailsLoading}
viewActionId={actionId}
setViewActionId={setActionId}
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"} />,
});
setAlert(false);
}
} 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 FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
import { useGetIOprepopulateDataQuery } from "../../../Services/io.service";
import UnderConstruction from "../../UnderConstruction";
import Destribution from "./Destribution";
const CreateIO = () => {
const id = useParams()?.id;
const { data, error, isLoading } = useGetIOprepopulateDataQuery();
// console.log(data?.data);
const enableNextTab = (index) => {
setTabs((prevTabs) => {
@@ -43,32 +42,38 @@ const CreateIO = () => {
{
label: "Investment documents",
Content: InvestmentDocument,
isDisabled: id ? false : true,
isDisabled: id ? true : true,
},
{
label: "Key merits",
Content: KeyMerits,
isDisabled: id ? false : true,
isDisabled: id ? true : true,
},
{
label: "IO artifacts",
Content: IOArtifacts,
isDisabled: id ? false : true,
isDisabled: id ? true : true,
},
{
label: "Investors",
Content: Investors,
isDisabled: id ? false : false,
// Content: UnderConstruction,
isDisabled: id ? true : true,
},
{
label: "IO Cash Details",
label: "IO Cash Detail",
Content: IOCashDetails,
isDisabled: id ? false : false,
isDisabled: id ? true : true,
},
{
label: "IO NAV Details",
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"}
pb={10}
>
<Box paddingInline={"12px"} mt={2}>
{id && <Box
ps={1}
pe={2} mt={2}>
{/* <span
onClick={() => navigate(-1)}
style={{ fontSize: "15px", cursor: "pointer" }}
>
<ArrowBackIcon cursor={"pointer"} /> Back
</span> */}
<ViewIOdataHeader />
</Box>
<ViewIOdataHeader isLoading={isLoading} data={data?.data} />
</Box>}
<Tabs
index={activeIndex}
onChange={(index) => setActiveIndex(index)}
mt={2}
ps={1}
pe={2}
>
<TabList>
{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 = () => {
return (
<div>Distribution</div>
const Destribution = () => {
const { navDetails, setNavDetails, IODetails } =
useContext(GlobalStateContext);
const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
console.log(IODetails?.ioNAVHistory);
const formatDate = (date) => {
return new Date(date).toLocaleDateString('en-GB', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
});
};
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
// Table setup
const tableHeadRow = [
// "Sr.No",
"Date",
"Amount",
"% of Investment"
];
// Table filter
const filteredData = IODetails?.distributionToInvestor?.filter((item) => {
const name = item?.transactionAmount;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches;
}).sort((b, a) => new Date(a.transactionDate) - new Date(b.transactionDate));
const extractedArray=filteredData?.map((item, index) => ({
id: item?.id,
"Sr.No": <Text
justifyContent={"start"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-start web-text-small"
>{item?.id}</Text>,
"Date": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{formatDate(item.transactionDate)}
</Text>
),
"Amount": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
<Badge ms={1} colorScheme="green" me={1}>$</Badge>
{`${parseFloat(item.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text>
),
"% of Investment": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{parseFloat(item.percentage).toFixed(2)}%
</Text>
),
}));
const handleDelete = () => {
const updatedNav = navDetails.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setNavDetails(updatedNav);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const Total = () => {
return (
<Table size="sm">
<Tbody>
<Tr backgroundColor="gray.50">
<Th
textAlign={"center"}
p={3}
width="60px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
Total
</Th>
<Th
textAlign={"center"}
p={3}
width="90px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
<Badge ms={1} colorScheme="green" me={1}>$</Badge>{IODetails?.total_distributeToInvestor_amt?.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</Th>
<Th
textAlign={"center"}
p={3}
width="100px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{" "}
</Th>
</Tr>
</Tbody>
</Table>
);
};
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,
Tooltip,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react";
import DataTable from "../../../Components/DataTable/DataTable";
@@ -21,7 +22,7 @@ import {
} from "@chakra-ui/icons";
import IOArtifactsAdd from "../IOArtifactsAdd";
import IOArtifactsVideo from "./IOArtifactsVideo";
import SetDisplayOrder from "./SetDisplayOrder";
import SetDisplayOrder from "./SetDisplayOrderKeyMerits";
import { useParams } from "react-router-dom";
import {
useDeleteImageArtifactsMutation,
@@ -30,8 +31,12 @@ import {
} from "../../../Services/io.service";
import { getFileNameFromPath } from "../../../Constants/Constants";
import ImageViewer from "../../../Components/ImageViewer";
import ToastBox from "../../../Components/ToastBox";
import SetDisplayOrderIOArtifactsImages from "./SetDisplayOrderIOArtifactsImages";
import SetDisplayOrderIOArtifactsVideo from "./SetDisplayOrderIOArtifactsVideo";
const IOArtifacts = ({ enableNextTab, index, data }) => {
const toast = useToast()
const params = useParams();
const id = params?.id;
@@ -107,10 +112,12 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
setIsLoadingBtn(true);
try {
const res = await deleteVideoArtifacts(id);
console.log(res?.data?.statusCode);
if (res?.data?.statusCode === 200) {
setDeleteAlertVideo(false);
setIsLoadingBtn(false);
toast({
render: () => <ToastBox message={res?.data?.message} status="error" />,
});
}
} catch (error) {
console.log(error);
@@ -121,10 +128,15 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
setIsLoadingBtn(true);
try {
const res = await deleteImageArtifacts(id);
console.log(res?.data?.statusCode);
console.log(res);
if (res?.data?.statusCode === 200) {
setDeleteAlertImage(false);
setIsLoadingBtn(false);
toast({
render: () => <ToastBox message={res?.data?.message} status="error" />,
});
}
} catch (error) {
console.log(error);
@@ -133,7 +145,14 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
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": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
@@ -170,7 +189,7 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
colorScheme={"forestGreen"}
>
<Link
href={"https://tanami.betadelivery.com/" + item?.artifactPathName}
href={import.meta.env.VITE_IMAGE_URL + item?.artifactPathName}
isExternal
>
<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 = [
"Sr.no",
"File Name",
@@ -243,7 +273,7 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
"Action",
];
const extractedArrayTwo = IObyID?.data?.artifactsVideo?.map(
const extractedArrayTwo = sortedDataVideo?.map(
(item, index) => ({
"Sr.no": (
<Text
@@ -342,7 +372,9 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
Manage IO Images
</Box>
<HStack>
<SetDisplayOrder data={IObyID?.data?.artifactsImage} />
{IObyID?.data?.artifactsImage?.length !== 0 &&<SetDisplayOrderIOArtifactsImages data={sortedDataImage} />}
<Button
leftIcon={<AddIcon />}
onClick={onOpen}
@@ -378,7 +410,7 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
Manage IO videos
</Box>
<HStack>
<SetDisplayOrder data={IObyID?.data?.artifactsImage} />
{IObyID?.data?.artifactsVideo?.length !== 0 &&<SetDisplayOrderIOArtifactsVideo data={sortedDataVideo} />}
<Button
leftIcon={<AddIcon />}
onClick={onOpenVideo}

View File

@@ -27,24 +27,27 @@ import ToastBox from "../../../Components/ToastBox";
const investmentVideoSchema = yup.object().shape({
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 id = params?.id
const [file, setFile] = useState("");
const [fileName, setFileName] = useState("");
const [ isLoading, setIsLoading] = useState(false)
const [isLoading, setIsLoading] = useState(false)
const [alert, setAlert] = useState(false);
const toast = useToast();
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 {
// data
// } = useGetArtifactsQuery(id)
@@ -63,7 +66,7 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, d
// useEffect to reset the form when `found` changes
useEffect(() => {
if (found) {
if (found && actionId) {
reset({
artifactName: found?.artifactName,
artifactStreamingURL: found?.artifactStreamingURL,
@@ -71,8 +74,6 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, d
}
}, [found, reset]);
console.log(watch());
const onSubmit = async (data) => {
setIsLoading(true)
@@ -80,38 +81,39 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, d
try {
if (found) {
const res = await updateVideoArtifacts({data, id: found?.id})
const res = await updateVideoArtifacts({ data, id: found?.id })
if (res?.data?.statusCode === 200) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
setAlert(false);
setIsLoading(false)
handleClose();
});
setAlert(false);
setIsLoading(false)
handleClose();
}
} else {
const res = await createArtifactsVideo({data, id})
const res = await createArtifactsVideo({ data, id })
if (res?.data?.statusCode === 200) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
setAlert(false);
setIsLoading(false)
handleClose();
});
setAlert(false);
setIsLoading(false)
setActionId(false);
handleClose();
}
}
} catch (error) {
console.log(error);
}
};
@@ -128,12 +130,16 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, d
onClose()
reset()
setActionId(false);
reset({
artifactName: "",
artifactStreamingURL: "",
});
}
return (
<>
<Drawer
size={"md"}
size={"md"}
isOpen={isOpen}
placement="right"
initialFocusRef={firstField}
@@ -146,7 +152,7 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, d
<DrawerBody>
<Stack spacing={4}>
<FormControl isInvalid={errors.artifactName}>
<FormControl isInvalid={errors.artifactName} isRequired={true}>
<FormLabel fontSize={"sm"}>Artifact Name</FormLabel>
<Controller
name="artifactName"
@@ -160,7 +166,7 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, d
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.artifactStreamingURL}>
<FormControl isInvalid={errors.artifactStreamingURL} isRequired={true}>
<FormLabel fontSize={"sm"}>Artifact Streaming URL</FormLabel>
<Controller
name="artifactStreamingURL"
@@ -200,7 +206,7 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, d
</DrawerContent>
</Drawer>
<CustomAlertDialog
isOpen={alert}
onClose={() => setAlert(false)}

View File

@@ -1,4 +1,6 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
@@ -9,22 +11,29 @@ import {
Text,
Th,
Tr,
useDisclosure,
useToast,
} 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 DataTable from "../../../Components/DataTable/DataTable";
import NormalTable from "../../../Components/DataTable/NormalTable";
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";
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 IOCashDetails = () => {
const toast = useToast();
const { caseDetails, setCaseDetails, slideFromRight } =
const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const { caseDetails, setCaseDetails, IODetails } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
@@ -43,6 +52,14 @@ const IOCashDetails = () => {
return () => clearTimeout(timer);
}, []);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
// Calculate totals
const totalAmount = caseDetails.reduce(
(acc, caseDetail) => acc + caseDetail.amount,
@@ -52,13 +69,13 @@ const IOCashDetails = () => {
// Table setup
const tableHeadRow = [
"Date",
"Particulars",
"Transaction type",
"Amount",
"Comments",
"Update by ",
"Update On",
];
const handleUpdateStatus = debounce((id) => {
setCaseDetails((prevSponser) =>
prevSponser.map((sponsor) =>
@@ -71,52 +88,60 @@ const IOCashDetails = () => {
}, 300);
// Table filter
const filteredData = caseDetails.filter((item) => {
const name = item.date;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches;
});
const filteredData = IODetails?.ioCashHistory
?.filter((item) => {
const name = item.transactionType;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches;
})
.sort((b, a) => new Date(a.createdAt) - new Date(b.createdAt));
const [ extractedArray, setExtractedArray ] = useState(filteredData?.map((item, index) => ({
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id,
"Date": (
Date: (
<Text
justifyContent={slideFromRight ? "right" : "center"}
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.date}
{formatDate(item?.transactionDate)}
</Text>
),
"Particulars": (
"Transaction type": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.particulars}
{item?.transactionType}
</Text>
),
"Amount": (
Amount: (
<Text
justifyContent={slideFromRight ? "right" : "center"}
justifyContent={"left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
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>
),
"Comments": (
Comments: (
<Text
justifyContent={slideFromRight ? "right" : "center"}
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
@@ -127,27 +152,54 @@ const IOCashDetails = () => {
),
"Update by ": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
gap={2}
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>
),
"Update On": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.updateOn}
{formatDate(item.updatedAt)}
</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 updatedSponsors = sponser.filter(
@@ -166,7 +218,7 @@ const IOCashDetails = () => {
return (
<Table size="sm">
<Tbody backgroundColor="gray.50">
<Tr >
<Tr>
<Th
textAlign={"center"}
p={3}
@@ -260,18 +312,45 @@ const IOCashDetails = () => {
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} />
</HStack>
<HStack display={"flex"} alignItems={"center"}>
<Button
onClick={() =>
exportToExcelNew(ioCashExporteDetails, "IO Cash History")
}
leftIcon={<LuFileSpreadsheet />}
colorScheme="forestGreen"
size={"sm"}
variant={"outline"}
rounded={"sm"}
fontSize={"xs"}
isDisabled={ioCashExporteDetails?.length === 0}
>
Export xls
</Button>
{IODetails?.isInvestedAmount ? (
<Button
onClick={onOpen}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add IO Cash
</Button>
) : null}
</HStack>
</HStack>
</Box>
<DataTable
<NormalTable
centered={true}
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
setData={setExtractedArray}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
@@ -287,6 +366,12 @@ const IOCashDetails = () => {
alertHandler={handleDelete}
isLoading={isLoading}
/>
<AddCashDetails
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
</Box>
);
};

View File

@@ -6,9 +6,6 @@ import { useForm } from "react-hook-form";
import * as yup from "yup";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
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 {
useCreateIOMutation,
useGetIOByIdQuery,
@@ -16,32 +13,19 @@ import {
} from "../../../Services/io.service";
import ToastBox from "../../../Components/ToastBox";
import {
Input,
Table,
Tbody,
Td,
Th,
Thead,
Tr,
useToast,
} from "@chakra-ui/react";
import { formatDate } from "../../Master/Sponser/Sponsers";
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";
import { formatDatee } from "../../../Components/FormField";
import { formatDateToYYYYMMDD, removeTrailingZeros } from "../../../Constants/Constants";
const schema = 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(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()
.required("IO name in Arabic is required")
.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")
.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()
.required("Goal amount is required")
.positive("Goal amount must be a positive number")
.min(1, "Goal amount must be at least 1"),
goalAmount: yup
.number()
.typeError("Goal Amount is must be number")
.required('Goal amount is required')
.positive('Goal amount must be a positive number'),
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"),
// minInvestmentAmount: yup
// .number()
@@ -83,7 +70,7 @@ const schema = yup.object().shape({
InvestmentDetails: yup.string().notRequired(),
comment: yup.string().notRequired()
.min(10, "Comment must be at least 10 characters long")
// .min(10, "Comment must be at least 10 characters long")
.max(100, "Comment must be at most 100 characters long"),
expectedReturn: yup
@@ -91,19 +78,29 @@ const schema = yup.object().shape({
.required("Expected return is required"),
});
const IODetails = ({ enableNextTab, index, data }) => {
const params = useParams();
const navigate = useNavigate();
const toast = useToast();
const handleInputChange = (index, newValue) => {
const handleInputChangeCreate = (index, newValue) => {
const updatedValues = [...values];
updatedValues[index].value = newValue;
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 ]
@@ -113,7 +110,7 @@ const IODetails = ({ enableNextTab, index, data }) => {
const id = params?.id;
// ======================[ Cotext Api ]
const { investmentType, sponser, IODetails, setIODetails } =
const { investmentType, sponser, setIOStatus, setIODetails, setIOloading } =
useContext(GlobalStateContext);
// ======================[ RTK Querry Api ]
@@ -123,9 +120,8 @@ const IODetails = ({ enableNextTab, index, data }) => {
isLoading: IObyIDisLoading,
error: IObyIDerror,
} = useGetIOByIdQuery(id, { skip: !id });
console.log(IObyID);
const [creatIO] = useCreateIOMutation();
const [updateIO] = useUpdateIOMutation();
@@ -147,43 +143,121 @@ const IODetails = ({ enableNextTab, index, data }) => {
});
const miniValue = data?.country?.map(
({ countryName, flagIcon, minInvestmentAmt, countryCode, id }, index) => {
({ countryName, flagIcon, minInvestmentAmt, countryCode, id, currency }, index) => {
return {
id:id,
country: countryName,
value: minInvestmentAmt,
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{
_id:id,
id:country_xid,
country: country?.countryName,
value: minInvestmentAmt,
value: removeTrailingZeros(minInvestmentAmt),
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);
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);
// ======================[ Validator filter ]
const {
control,
reset,
watch,
setValue,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(schema),
resolver: yupResolver(id ? schemaEdit : schema),
});
useEffect(() => {
setIOloading(IObyIDisLoading)
setIODetails({
...IObyID?.data,
});
@@ -194,23 +268,22 @@ const IODetails = ({ enableNextTab, index, data }) => {
descriptionEnglish: IObyID?.data?.descriptionEnglish,
descriptionArabic: IObyID?.data?.descriptionArabic,
goalAmount: IObyID?.data?.goalAmount,
closingDate: IObyID?.data?.closingDate,
closingDate: formatDatee(IObyID?.data?.closingDate),
holdingPeriod: IObyID?.data?.holdingPeriod,
ISIN: IObyID?.data?.ISIN,
comment: IObyID?.data?.comment,
expectedReturn: IObyID?.data?.expectedReturn,
investmentType_xid: IObyID?.data?.investmentType_xid,
investmentType_xid: IObyID?.data?.investmentType_xid,
InvestmentDetails: IObyID?.data?.InvestmentDetails,
minInvestmentAmount: IObyID?.data?.minInvestmentAmount,
holdingPeriodArabic: IObyID?.data?.minInvestmentAmount,
expectedReturnArabic: IObyID?.data?.minInvestmentAmount,
isShariah: IObyID?.data?.isShariah
});
}
}, [id, IObyID]);
// const minInvestmentById =
//=======================[ Creator ]
const formFields = [
{
@@ -221,6 +294,8 @@ const IODetails = ({ enableNextTab, index, data }) => {
isRequired: true,
section: " ",
width: "49%",
maxLength:150,
helperText:`Maximum length should be 150 characters. You have entered ${watch()?.investmentNameEnglish?.length || 0} characters.`
},
{
label: "IO Name (Arabic)",
@@ -231,6 +306,8 @@ const IODetails = ({ enableNextTab, index, data }) => {
arabic: true,
section: " ",
width: "49%",
maxLength:150,
helperText:`Maximum length should be 150 characters. You have entered ${watch()?.investmentNameArabic?.length || 0} characters.`
},
{
label: "Description",
@@ -240,7 +317,8 @@ const IODetails = ({ enableNextTab, index, data }) => {
isRequired: true,
section: " ",
width: "49%",
maxLength:1000
maxLength:1000,
helperText:`Maximum length should be 1000 characters. You have entered ${watch()?.descriptionEnglish?.length || 0} characters.`
},
{
label: "Description (Arabic)",
@@ -251,8 +329,69 @@ const IODetails = ({ enableNextTab, index, data }) => {
arabic: true,
section: " ",
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",
placeHolder: "Select option",
@@ -265,7 +404,7 @@ const IODetails = ({ enableNextTab, index, data }) => {
value: IObyID?.data?.investmentType_xid,
},
{
label: "Sponsorer Name",
label: "Sponsor Name",
placeHolder: "Select option",
name: "sponserName",
type: "select",
@@ -288,32 +427,14 @@ const IODetails = ({ enableNextTab, index, data }) => {
{
label: "Closing Date",
name: "closingDate",
value: formatDate(IObyID?.data?.closingDate),
// value: "IObyID?.data?.closingDate",
type: "date",
isRequired: true,
section: " ",
width: "32.3%",
helperText: IObyID && `Current closing date is : ${formatDate(IObyID?.data?.closingDate)}`
},
{
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,
dateValue:formatDatee(IObyID?.data?.closingDate),
// helperText: IObyID && `Current closing date is : ${formatDate(IObyID?.data?.closingDate)}`
closingDate:true
},
{
label: "ISIN",
@@ -332,6 +453,8 @@ const IODetails = ({ enableNextTab, index, data }) => {
section: " ",
width: "32.3%",
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",
type: "table",
section: " ",
width: "100%",
width: "100%",
isRequired: true,
options: investmentTypeOptions,
type: "table",
handleInputChange: handleInputChange,
handleInputChange:id ? handleInputChangeEdit : handleInputChangeCreate,
value: values,
},
@@ -357,154 +479,10 @@ const IODetails = ({ enableNextTab, index, data }) => {
width: "100%",
options: investmentTypeOptions,
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 { section } = field;
if (!groups[section]) {
@@ -515,27 +493,30 @@ const IODetails = ({ enableNextTab, index, 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 {
id:_id,
country_xid:id,
minInvestmentAmt: Number(value)
}
})
// console.log(formatDateToYYYYMMDD(data.closingDate));
const formData = {
...data,
investmentType_xid: Number(data.investmentType),
sponsor_xid: Number(data.sponserName),
minInvestmentAmt:updatedMinAmount
minInvestmentAmt:updatedMinAmount,
closingDate: formatDateToYYYYMMDD(data.closingDate)
};
// console.log(formData);
// console.log(formData);
if (id) {
console.log("========================",formData);
const res = await updateIO({ data: formData, id });
console.log(res);
if (res?.data?.statusCode === 200) {
@@ -546,6 +527,11 @@ const IODetails = ({ enableNextTab, index, data }) => {
navigate(`/view-io/${id}`);
enableNextTab(index);
} 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);
toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
@@ -553,6 +539,7 @@ const IODetails = ({ enableNextTab, index, data }) => {
}
} else {
try {
console.log("========================",formData);
const res = await creatIO(formData);
console.log(res?.error?.status);
if (res?.data?.statusCode === 200) {
@@ -567,6 +554,11 @@ const IODetails = ({ enableNextTab, index, data }) => {
toast({
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) {
setIsLoading(false);
@@ -592,8 +584,9 @@ const IODetails = ({ enableNextTab, index, data }) => {
};
return IObyIDisLoading ? (
<FullscreenLoaders />
<FullscreenLoaders height={'70vh'} />
) : (
<FormInputMain
p={0.1}
w={250}
@@ -604,7 +597,13 @@ const IODetails = ({ enableNextTab, index, data }) => {
onSubmit={handleSubmit(onSubmit)}
btnLoading={isLoading}
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 GlobalStateContext from '../../../Contexts/GlobalStateContext';
import { Box, HStack, Input,Text, Table, Tbody, Th, Tr } from '@chakra-ui/react';
import { OPACITY_ON_LOAD } from '../../../Layout/animations';
import Pagination from '../../../Components/Pagination';
import DataTable from '../../../Components/DataTable/DataTable';
import CustomAlertDialog from '../../../Components/CustomAlertDialog';
import React, { useContext, useEffect, useRef, useState } from "react";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import {
Box,
HStack,
Input,
Text,
Table,
Tbody,
Th,
Tr,
Avatar,
useDisclosure,
Button,
Badge,
} from "@chakra-ui/react";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import Pagination from "../../../Components/Pagination";
import NormalTable from "../../../Components/DataTable/NormalTable";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import { formatDatee } from "../../../Components/FormField";
import { AddIcon } from "@chakra-ui/icons";
import AddIONav from "./AddIONav";
import { formatDate } from "../../Master/Sponser/Sponsers";
import { LuFileSpreadsheet } from "react-icons/lu";
import { exportToExcel, exportToExcelNew } from "../../../Constants/Constants";
const IONAVDetails = () => {
const { navDetails, setNavDetails, slideFromRight } =
const { navDetails, setNavDetails, IODetails } =
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 firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
console.log(IODetails?.ioNAVHistory);
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
@@ -27,90 +49,163 @@ const IONAVDetails = () => {
return () => clearTimeout(timer);
}, []);
// Table setup
const tableHeadRow = [
"Sr.No",
"As On Date",
"IO NAV Value",
"Comments",
"Update by ",
"Update On",
];
// Table setup
const tableHeadRow = [
// "Sr.No",
"Valuation Date",
"NAV",
"Last NAV update",
"Investment Closed",
"Comments",
"Update by ",
// "Update On",
];
// Table filter
const filteredData = navDetails?.filter((item) => {
const name = item.updateBy;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches;
});
// Table filter
const filteredData = IODetails?.ioNAVHistory
?.filter((item) => {
const name = item.transactionType;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches;
})
.reverse()
// .sort((b, a) => new Date(a.transactionDate) - new Date(b.transactionDate));
const [ extractedArray, setExtractedArray ] = useState(filteredData?.map((item, index) => ({
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id,
"Sr.No": index +1,
"As On Date": (
"Sr.No": index + 1,
"Valuation Date": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.date}
{/* {/ {formatDatee(item.transactionDate)} /} */}
{formatDate(item?.transactionDate)}
</Text>
),
"IO NAV Value": (
NAV: (
<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"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{`$${item.IONavValue}`}
{item.previousNAVvalue && `${item.previousNAVvalue}`}
</Text>
),
"Comments": (
"Investment Closed": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.initialNAVvalue && `${item.initialNAVvalue}`}
</Text>
),
Comments: (
// <Text
// justifyContent={"center"}
// as={"span"}
// color={"teal.900"}
// fontWeight={"500"}
// className="d-flex align-items-center web-text-small"
// >
// {item.comments}
// </Text>
<Text
as={"span"}
color={"teal.900"}
fontWeight={"500"}
maxWidth="200px" // Adjust width as needed
display="block" // Ensure block display for proper truncation
overflow="hidden"
isTruncated
textOverflow="ellipsis"
>
{item.comments}
</Text>
),
"Update by ": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
gap={2}
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>
),
"Update On": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.updateOn}
{formatDate(item.updatedAt)}
</Text>
),
})));
}));
const customHeaders = [
{ label: "ID", key: "id" },
{ label: "Valuation Date", key: "transactionDate" },
{ label: "NAV", key: "transactionAmount" },
{ label: "Last NAV update", key: "previousNAVvalue" },
{ label: "Investment Closed", key: "initialNAVvalue" },
{ label: "Comments", key: "comments" },
// { label: "Update by", key: "creator" },
{ label: "Transaction Type", key: "transactionType" },
// { label: "Comments", key: "comments" },
// Add more headers as needed
];
console.log(IODetails?.ioNAVHistory);
const ioNavExport = IODetails?.ioNAVHistory?.map((item, index) => ({
"ID": item?.id, // Keep as integer if it's already a number
"Valuation Date": formatDate(item?.transactionDate), // Assuming this is a date, no conversion needed
"NAV": parseFloat(item?.transactionAmount) || 0, // Convert to float
"Last NAV update": parseFloat(item?.previousNAVvalue) || 0, // Convert to float
"Investment Closed": parseFloat(item?.initialNAVvalue) || 0, // Convert to float
"Comments": item?.comments, // Keep as string
"Transaction Type": item?.transactionType, // Keep as string
"Update by ": item?.creator?.firstName, // Keep as string
// "Update On": formatDate(item?.updatedAt) // Assuming this is a date, no conversion needed
}));
const handleDelete = () => {
const updatedNav = navDetails.filter(
(sponsor) => sponsor.id !== actionId
);
const updatedNav = navDetails.filter((sponsor) => sponsor.id !== actionId);
setTimeout(() => {
setNavDetails(updatedNav);
@@ -120,57 +215,84 @@ const IONAVDetails = () => {
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}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} />
{IODetails?.isInvestedAmount ? (
<Button
onClick={onOpen}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add IO Nav
</Button>
) : null}
</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>
);
};
<DataTable
centered={true}
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
setData={setExtractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
caption={"Tanami v1.0"}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
</Box>
)
}
export default IONAVDetails
export default IONAVDetails;

View File

@@ -33,12 +33,12 @@ import {
import ToastBox from "../../../Components/ToastBox";
import { getFileNameFromPath } from "../../../Constants/Constants";
import { TbFileTypeDocx } from "react-icons/tb";
import SetDisplayOrder from "./SetDisplayOrder";
import SetDisplayOrder from "./SetDisplayOrderKeyMerits";
import SetDisplayOrderIODocuments from "./SetDisplayOrderIODocuments";
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) => {
if (!response.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 id = params?.id;
const { slideFromRight, create, setCreate } = useContext(GlobalStateContext);
const { slideFromRight, create, setCreate, IODetails } = useContext(GlobalStateContext);
const firstField = useRef();
const secondField = useRef();
const thirdField = useRef();
@@ -101,13 +101,18 @@ const InvestmentDocument = ({ control, errors, enableNextTab, index }) => {
const [deleteIODocs] = useDeleteIODocsMutation();
const {
data,
error,
isLoading: isIODocLoading,
} = useGetInvestmentDocumentsQuery(id, {
skip: !id,
});
// const {
// data,
// error,
// isLoading: isIODocLoading,
// } = useGetInvestmentDocumentsQuery(id, {
// skip: !id,
// });
const tableHeadRow = ["Sr.no", "Type", "File Name", "Document", "Action"];
@@ -126,10 +131,14 @@ const InvestmentDocument = ({ control, errors, enableNextTab, index }) => {
});
}, 300);
const filteredData = data?.data?.filter((item) =>
const filteredData = IODetails?.documents?.filter((item) =>
item?.documentName?.toLowerCase().includes(searchTerm.toLowerCase())
);
const sortedData = filteredData?.sort(
(a, b) => a.displayOrder - b.displayOrder
);
const handleView = (id) => {
setActionId(id);
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": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
@@ -294,7 +303,9 @@ const InvestmentDocument = ({ control, errors, enableNextTab, index }) => {
return (
<Box>
<Box display="flex" justifyContent="end" mb={4} gap={2}>
<SetDisplayOrder data={filteredData} />
{filteredData?.length !== 0 &&<SetDisplayOrderIODocuments data={filteredData} />}
<Button
leftIcon={<AddIcon />}
onClick={onOpen}
@@ -319,7 +330,7 @@ const InvestmentDocument = ({ control, errors, enableNextTab, index }) => {
secondField={secondField}
/>
<InvestmentEdit
data={data?.data}
data={IODetails?.documents}
id={actionId}
isOpen={isEditOpen}
onClose={onEditClose}

View File

@@ -4,6 +4,7 @@ import {
Box,
Button,
HStack,
Icon,
Input,
Menu,
MenuButton,
@@ -20,25 +21,50 @@ import {
Thead,
Tooltip,
Tr,
keyframes,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
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 { Link, Link as RouterLink } from "react-router-dom";
import { Link, Link as RouterLink, useParams } from "react-router-dom";
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";
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 Investors = () => {
const Investors = ({ data }) => {
const params = useParams();
const id = params?.id;
const toast = useToast();
const { investors, setInvestors, slideFromRight } =
const { investors, setInvestors, slideFromRight, IODetails } =
useContext(GlobalStateContext);
const [isRefetchLoading, setIsRefetchLoading] = useState(false);
const { isLoading: IObyIDisLoading, refetch } = useGetIOByIdQuery(id, {
skip: !id,
});
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
@@ -84,6 +110,7 @@ const Investors = () => {
"Market Value",
"Return on Investment",
"Distribution",
"Distribution Percent",
"Total Return",
"Total return on Investment",
];
@@ -99,16 +126,50 @@ const Investors = () => {
});
}, 300);
// Table filter
const filteredData = investors.filter((item) => {
const filteredData = IODetails?.investors?.filter((item) => {
const clientId = item?.clientReference_id;
const name = item.firstName;
const lastName = item?.lastName
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;
});
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,
"Client ID": (
<Text
@@ -118,7 +179,7 @@ const Investors = () => {
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.clientId}
{item?.clientReference_id}
</Text>
),
"First name": (
@@ -145,16 +206,23 @@ const Investors = () => {
),
"Investment amount": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
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>
),
"Percentage": (
Percentage: (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
@@ -162,18 +230,24 @@ const Investors = () => {
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.percentage}
{item.Investor_Holidings} %
</Text>
),
"Market Value": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
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>
),
"Return on Investment": (
@@ -185,10 +259,28 @@ const Investors = () => {
h={6}
className="d-flex align-items-center web-text-small"
>
{item.returnOnInvestment}
{item.Return_On_Investment || 0} %
</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
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
@@ -196,18 +288,29 @@ const Investors = () => {
fontWeight={"500"}
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>
),
"Total Return": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
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>
),
"Total return on Investment": (
@@ -218,10 +321,10 @@ const Investors = () => {
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.totalReturnOnInvestment}
{item.Total_Return_On_Investment || 0} %
</Text>
),
})));
}));
const handleDelete = () => {
const updatedSponsors = sponser.filter(
@@ -240,7 +343,7 @@ const Investors = () => {
return (
<Table size="sm">
<Tbody backgroundColor="gray.50">
<Tr >
<Tr>
<Th
textAlign={"center"}
p={3}
@@ -357,6 +460,17 @@ const Investors = () => {
);
};
const handleRefresh = async () => {
setIsRefetchLoading(true);
await refetch();
setIsRefetchLoading(false);
};
console.log(IODetails?.investors);
return (
<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
@@ -366,29 +480,90 @@ const Investors = () => {
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)}
/>
<Box display={'flex'} gap={3}>
<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} />
<Button
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>
</Box>
<DataTable
<NormalTable
centered={true}
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
setData={setExtractedArray}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}

View File

@@ -32,22 +32,22 @@ import {
import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
import ToastBox from "../../../Components/ToastBox";
import KeyMeritsEdit from "../KeyMeritsEdit";
import SetDisplayOrder from "./SetDisplayOrder";
import SetDisplayOrder from "./SetDisplayOrderKeyMerits";
const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
const toast = useToast();
const params = useParams();
// =====================[ variables ]
// =====================[ variables ]
const id = params?.id;
const { data, isLoading, error } = useGetKeyMeritsQuery(id, {
skip: !id,
});
// const { data, isLoading, error } = useGetKeyMeritsQuery(id, {
// skip: !id,
// });
console.log(data?.data);
const { IODetails} = useContext(GlobalStateContext);
const { keyMerits, setKeyMerits, slideFromRight } =
useContext(GlobalStateContext);
const firstField = useRef();
const [searchTerm, setSearchTerm] = useState("");
@@ -57,6 +57,7 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
const [isBtnLoading, setIsBtnLoading] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const { isOpen, onOpen, onClose } = useDisclosure();
const {
isOpen: isEditOpen,
onOpen: onEditOpen,
@@ -66,22 +67,11 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
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)
const name = item.meritsHeader;
const name = item?.meritsHeader;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
@@ -90,7 +80,7 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
// console.log(filteredData);
const sortedData = filteredData?.sort(
(a, b) => a.displayOder - b.displayOder
(a, b) => a.displayOrder - b.displayOrder
);
const handleDelete = async () => {
@@ -107,11 +97,13 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
} catch (error) {}
};
const extractedArray = sortedData?.map((item, index) => ({
id: item.id,
"Sr.no": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
justifyContent={"left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
@@ -122,7 +114,7 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
),
Title: (
<Text
justifyContent={slideFromRight ? "right" : "left"}
justifyContent={"left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
@@ -139,14 +131,14 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
</Box>
),
Icon: item?.icon?.iconFilePath && (
<Image
<Image
rounded={"md"}
// bg={"#003B14"}
display={"flex"}
p={1}
justifyContent={"center"}
alignItems={"center"}
src={" https://tanami.betadelivery.com/" + item?.icon?.iconFilePath}
src={import.meta.env.VITE_IMAGE_URL+ item?.icon?.iconFilePath}
w={8}
h={8}
/>
@@ -224,7 +216,7 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
),
}));
return isLoading ? (
return false ? (
<FullscreenLoaders />
) : (
<Box>
@@ -241,7 +233,9 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
/> */}
<Box display={"flex"} gap={2} as="span">
<SetDisplayOrder data={filteredData} />
{filteredData?.length !== 0 &&<SetDisplayOrder data={filteredData} />}
<Button
leftIcon={<AddIcon />}
onClick={onOpen}
@@ -269,7 +263,7 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
isOpen={isEditOpen}
onClose={onEditCloseOpen}
firstField={firstField}
data={data?.data}
data={IODetails?.keyMerits}
/>
</Box>
<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 ToastBox from "../../../Components/ToastBox";
const SetDisplayOrder = ({ data }) => {
const SetDisplayOrderKeyMerits = ({ data, }) => {
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const [isLoading, setIsLoading] = useState(false);
@@ -57,16 +57,20 @@ const SetDisplayOrder = ({ data }) => {
</Box>
),
Icon: item?.icon?.iconFilePath && (
<Box
display={"flex"}
justifyContent={"center"}
alignItems={"center"}>
<Image
rounded={"md"}
display={"flex"}
p={1}
justifyContent={"center"}
alignItems={"center"}
src={"https://tanami.betadelivery.com/" + item?.icon?.iconFilePath}
src={import.meta.env.VITE_IMAGE_URL + item?.icon?.iconFilePath}
w={8}
h={8}
/>
/></Box>
),
}));
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();
// =====================[ variables ]
// const id = params?.id;
console.log(id);
console.log(data);
const filterObject = data?.find((item) => item?.id === id);
console.log(filterObject);
const getFileTitle = (type) => {
switch (type) {
case "application/pdf":
@@ -74,12 +69,16 @@ const InvestmentEdit = ({ isOpen, onClose, thirdField, id, data }) => {
resolver: yupResolver(investmentDocSchema),
});
console.log(errors);
// useEffect to reset the form when `found` changes
useEffect(() => {
if (filterObject) {
reset({
document: filterObject?.documentPath,
fileName: filterObject?.documentName,
documentNameArabic: filterObject?.documentNameArabic,
});
}
}, [filterObject, reset]);
@@ -89,6 +88,7 @@ const InvestmentEdit = ({ isOpen, onClose, thirdField, id, data }) => {
reset({
fileName: filteredObject?.fileName,
document: filteredObject?.document,
documentNameArabic: filterObject?.documentNameArabic,
Type: filteredObject?.Type,
});
}
@@ -158,6 +158,7 @@ const InvestmentEdit = ({ isOpen, onClose, thirdField, id, data }) => {
if (Object.keys(errors).length === 0) {
const formData = new FormData();
formData.append("documentName", data.fileName);
formData.append("documentNameArabic", data.documentNameArabic);
typeof data?.document !== "string"
? formData.append("document", data?.document[0])
: null;
@@ -171,7 +172,6 @@ const InvestmentEdit = ({ isOpen, onClose, thirdField, id, data }) => {
setFile(selectedFile);
};
console.log(filteredObject);
return (
<Drawer
@@ -200,6 +200,27 @@ const InvestmentEdit = ({ isOpen, onClose, thirdField, id, data }) => {
</Text>
)}
</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}>
<FormLabel fontSize="sm">Document</FormLabel>
<Input
@@ -209,6 +230,7 @@ const InvestmentEdit = ({ isOpen, onClose, thirdField, id, data }) => {
className="form-control"
type="file"
size="sm"
accept=".pdf, .doc, .docx"
onChange={handleFileChange}
/>
{errors.document && (

View File

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

View File

@@ -11,13 +11,15 @@ import {
FormControl,
FormErrorMessage,
FormLabel,
Icon,
Image,
Input,
Stack,
Text,
useToast,
} from "@chakra-ui/react";
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 { yupResolver } from "@hookform/resolvers/yup";
import CustomAlertDialog from "../../Components/CustomAlertDialog";
@@ -27,9 +29,13 @@ import {
} from "../../Services/io.service";
import { useParams } from "react-router-dom";
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({
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"),
});
@@ -41,8 +47,6 @@ const IOArtifactsAdd = ({
setActionId,
data,
}) => {
console.log(actionId);
console.log(data);
const params = useParams();
const id = params?.id;
const [file, setFile] = useState(null);
@@ -55,7 +59,7 @@ const IOArtifactsAdd = ({
const [updateImageArtifacts] = useUpdateImageArtifactsMutation();
const found = data?.find((item) => item?.id === actionId);
console.log(found);
const fileInputRef = useRef(null);
const {
control,
@@ -78,37 +82,48 @@ const IOArtifactsAdd = ({
}
}, [found, reset]);
console.log(watch());
const onSubmit = async (data) => {
console.log("hit");
setIsLoading(true);
const formData = new FormData();
formData.append("artifactName", data.artifactName);
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 {
if (found) {
const res = await updateImageArtifacts({
data: formData,
id: found?.id,
});
console.log(res?.error);
if (res?.data?.statusCode === 200) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
reset();
setFile(null);
setIsLoading(false);
setAlert(false);
setPreview(null);
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) {
@@ -126,7 +141,10 @@ const IOArtifactsAdd = ({
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
reset();
reset({
artifactName: "",
artifactPathName: "",
});
setFile(null);
setIsLoading(false);
setAlert(false);
@@ -154,7 +172,11 @@ const IOArtifactsAdd = ({
};
const handleSave = () => {
if (!alert) {
setAlert(true)
}else{
handleSubmit(onSubmit)();
}
};
const handleClose = () => {
@@ -169,6 +191,20 @@ const IOArtifactsAdd = ({
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 (
<>
<Drawer
@@ -182,93 +218,141 @@ const IOArtifactsAdd = ({
<DrawerContent>
<DrawerCloseButton />
<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>
<Stack spacing={4}>
<FormControl isInvalid={errors.artifactName}>
<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 &&
<FormControl
isInvalid={
!preview &&
errors.artifactPathName?.message &&
errors.artifactPathName?.message}
</FormErrorMessage>
{preview && (
<Image
rounded={"md"}
src={preview}
alt="Image Preview"
mt={2}
errors.artifactPathName
}
isRequired={true}
>
<FormLabel fontSize={"sm"}>Artifact Image (600 X 300)</FormLabel>
<Input
type="file"
accept=".jpg,.jpeg,.png"
onChange={handleFileChange}
fontSize={"sm"}
size={"sm"}
className="form-control"
ref={fileInputRef} // Set the ref here
/>
)}
{found && !preview && (
<Image
rounded={"md"}
src={
"https://tanami.betadelivery.com/" +
watch()?.artifactPathName
}
alt="Image Preview"
mt={2}
/>
)}
</FormControl>
</Stack>
</DrawerBody>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{!preview &&
errors.artifactPathName?.message &&
errors.artifactPathName?.message}
</FormErrorMessage>
{preview && (
<>
<Image
rounded={"md"}
src={preview}
alt="Image Preview"
mt={3}
width={300}
height={150}
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>
<Button
variant="outline"
colorScheme={"forestGreen"}
rounded={"sm"}
size={"sm"}
mr={3}
onClick={onClose}
>
Cancel
</Button>
<DrawerFooter mt={5}>
<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>
<Button
colorScheme={"forestGreen"}
rounded={"sm"}
size={"sm"}
// onClick={() => setAlert(true)}
onClick={handleSave}
// type="submit"
>
Save
</Button>
</DrawerFooter>
</Box>
</DrawerContent>
</Drawer>
<CustomAlertDialog
isOpen={alert}
onClose={() => setAlert(false)}
onClose={handleAlertClose}
alertHandler={handleSave}
message={"Are you sure you want to add this artifact?"}
isLoading={loading}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,4 @@
import React, { useContext, useEffect, useState } from "react";
import {
Box,
Button,
@@ -12,84 +13,245 @@ import {
ModalHeader,
ModalOverlay,
Text,
Textarea,
useToast,
} from "@chakra-ui/react";
import { Controller, useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import { useParams } from "react-router-dom";
import { useAmountIvestmentMutation } from "../../../../Services/io.service";
import ToastBox from "../../../../Components/ToastBox";
import CurrencyInput from "../../../../Components/CurrencyInput";
// 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 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 (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader fontSize={'md'}>Amount Invested</ModalHeader>
<ModalHeader fontSize={"md"}>Amount Invested</ModalHeader>
<ModalCloseButton />
<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>
<form onSubmit={handleSubmit(onSubmit)}>
<FormControl
mb={"15px"}
isInvalid={!!errors.transactionDate}
isRequired
>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
Date
</FormLabel>
<Input
type="date"
{...register("transactionDate")}
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"} >
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>Amount</FormLabel>
<Input
size="sm"
rounded={'sm'}
textAlign={'end'}
readOnly
value={"$ 100000"}
focusBorderColor="forestGreen.300"
fontSize={"sm"} placeholder="$00.00" />
</FormControl>
<FormControl
mb={"15px"}
isInvalid={!!errors.Total_Amount}
isReadOnly
>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
Amount
</FormLabel>
<Input
type="text"
value={formatCurrency(watch("Total_Amount"))}
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"} >
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>Amount to invest</FormLabel>
<Input
size="sm"
rounded={'sm'}
textAlign={'end'}
focusBorderColor="forestGreen.300"
fontSize={"sm"} placeholder="$00.00" />
</FormControl>
<FormControl
mb={"15px"}
isInvalid={!!errors.amountInvested}
isRequired
>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
Amount to invest
</FormLabel>
{/* <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"}>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
IO Cash
</FormLabel>
<Input
size="sm"
rounded={'sm'}
placeholder="$00.00"
focusBorderColor="forestGreen.300"
fontSize={"sm"} />
</FormControl>
<FormControl mb={"15px"} isInvalid={!!errors.IoCash}>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
IO Cash
</FormLabel>
<Input
type="text"
value={formatCurrency(watch("IoCash"))}
size="sm"
rounded={"sm"}
focusBorderColor="forestGreen.300"
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>
<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>
</Modal>
);

View File

@@ -1,7 +1,10 @@
import {
Alert,
AlertIcon,
Box,
Button,
FormControl,
FormErrorMessage,
FormLabel,
HStack,
Input,
@@ -19,12 +22,93 @@ import {
Textarea,
Th,
Tr,
useToast,
} from "@chakra-ui/react";
import DataTable from "../../../../Components/DataTable/DataTable";
import { useState } from "react";
import NormalData from "../../../../Components/DataTable/NormalTable";
import { useContext, useState } from "react";
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 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(
"is-positive",
"Amount should be greater than zero",
(value) => parseFloat(value) > 0 // Check if the amount is greater than zero
)
.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]================================================================
const tableHeadRow = [
"Sr No.",
@@ -32,96 +116,94 @@ const DistributionInvestor = ({ isOpen, onClose }) => {
"First name",
"Last Name",
"Amount",
"%",
"($)",
"Holding (%)",
"Distriution Amt($)",
"Yeild (%)",
];
const filteredData = [
{
id: 1,
},
{
id: 1,
},
{
id: 1,
},
{
id: 1,
},
];
const [extractedArray, setExtractedArray] = useState(
filteredData?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Box
w={9}
display={"flex"}
alignItems={"center"}
isTruncated={true}
h={25}
>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{index + 1}
</Text>
</Box>
),
"Client Id": (
<Box w={100} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
BH00000001
</Text>
</Box>
),
"First name": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
Faisal
</Text>
</Box>
),
"Last Name": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
Aljalahma
</Text>
</Box>
),
Amount: (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
$100,000 /-
</Text>
</Box>
),
"%": (
<Box minW={19} isTruncated={true}>
<Text
textAlign={"right"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
>
26.0 %
</Text>
</Box>
),
"($)": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
$100,000 /-
</Text>
</Box>
),
}))
);
const extractedArray = calcualtedData?.data?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Box
w={9}
display={"flex"}
alignItems={"center"}
isTruncated={true}
h={25}
>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{index + 1}
</Text>
</Box>
),
"Client Id": (
<Box w={100} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.clientId}
</Text>
</Box>
),
"First name": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.firstName}
</Text>
</Box>
),
"Last Name": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.lastName}
</Text>
</Box>
),
Amount: (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.amount?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</Box>
),
"Holding (%)": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.investor_holidings?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}%
</Text>
</Box>
),
"Distriution Amt($)": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.distribution_amt?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</Box>
),
"Yeild (%)": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.distribution_per?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}%
</Text>
</Box>
),
}));
const Total = () => {
return (
<Table size="sm">
<Tbody backgroundColor="gray.50">
<Tr>
<Tbody>
<Tr backgroundColor="gray.50">
<Th
textAlign={"left"}
p={3}
@@ -175,7 +257,10 @@ const DistributionInvestor = ({ isOpen, onClose }) => {
wordBreak="normal"
overflowWrap="normal"
>
$100200
{calcualtedData?.totalInvestedAmt?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Th>
<Th
textAlign={"left"}
@@ -185,8 +270,9 @@ const DistributionInvestor = ({ isOpen, onClose }) => {
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
opacity={0}
>
100.0%
{calcualtedData?.distributed_per?.toFixed(2)}%
</Th>
<Th
textAlign={"center"}
@@ -197,7 +283,10 @@ const DistributionInvestor = ({ isOpen, onClose }) => {
wordBreak="normal"
overflowWrap="normal"
>
$100230
{calcualtedData?.distributed_amt?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Th>
</Tr>
</Tbody>
@@ -205,54 +294,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 (
<Modal size={"xl"} isOpen={isOpen} onClose={onClose}>
<Modal size={"xl"} isOpen={isOpen} onClose={handleClose}>
<ModalOverlay />
<ModalContent maxW={1000}>
<ModalHeader fontSize={"md"}>Distribution To Investor Transaction</ModalHeader>
<ModalContent maxW={1200}>
<ModalHeader fontSize={"md"}>
Distribution To Investor Transaction
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text as="label" mb='5px' fontSize='sm' fontWeight={500}>Amount to Distribute</Text>
<HStack mb={4}>
<Input placeholder="$00.00" size={"sm"} className="col" />
{/* <Text as="label" mb="5px" fontSize="sm" fontWeight={500}>
Amount to Distribute
</Text> */}
<HStack onSubmit={handleSubmit(onSubmit)} as={"form"} mb={4}>
{/* <Input placeholder="$00.00" size={"sm"} className="col" /> */}
<Button
// leftIcon={<AddIcon />}
size={"sm"}
rounded={"sm"}
colorScheme="forestGreen"
>
Calculate
</Button>
<FormControl isInvalid={errors.amount} isRequired>
<FormLabel textAlign={"right"} fontSize={"sm"}>
{" "}
Amount to Distribute
</FormLabel>
<Box
display={"flex"}
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>
<DataTable
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
setData={setExtractedArray}
caption={<Total />}
// isLoading={isLoading}
/>
{calcualtedData ? (
<NormalData
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
// total={<Total />}
// isLoading={isLoading}
/>
) : (
<Alert status="info" fontSize={"sm"} rounded={"sm"}>
<AlertIcon />
Please enter amount to calcutale Distribution!
</Alert>
)}
</ModalBody>
<ModalFooter pt={0}>
<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>
{isCalcualtedData ? (
<>
<Button
bg={"hsla(139, 100%, 14%, 1)"}
mr={3}
color={"#fff"}
_hover={{
bg: "hsl(139deg 98.99% 26.59%)",
}}
size={"sm"}
rounded={"sm"}
onClick={handleSubmit(onFinalSubmit)}
isLoading={isFinalCalculateLoading}
>
Save
</Button>
<Button size={"sm"} rounded={"sm"} mr={3} onClick={onClose}>
Close
</Button>
</>
) : (
""
)}
</ModalFooter>
</ModalContent>
</Modal>

View File

@@ -1,7 +1,10 @@
import {
Alert,
AlertIcon,
Box,
Button,
FormControl,
FormErrorMessage,
FormLabel,
HStack,
Input,
@@ -19,115 +22,208 @@ import {
Textarea,
Th,
Tr,
useToast,
} from "@chakra-ui/react";
import DataTable from "../../../../Components/DataTable/DataTable";
import { useState } from "react";
import NormalData from "../../../../Components/DataTable/NormalTable";
import { useContext, useState } from "react";
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 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]================================================================
const tableHeadRow = [
"Sr No.",
"Client Id",
"First name",
"Last Name",
"Invested Amount",
"Distribution %",
"Exit Amount",
"Amount",
"Holding (%)",
"Exit Amt($)",
];
const filteredData = [
{
id: 1,
},
{
id: 1,
},
{
id: 1,
},
{
id: 1,
},
{
id: 1,
},
{
id: 1,
},
];
const [extractedArray, setExtractedArray] = useState(
filteredData?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Box
w={9}
display={"flex"}
alignItems={"center"}
isTruncated={true}
h={25}
>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{index + 1}
</Text>
</Box>
),
"Client Id": (
<Box w={100} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
BH0000000
</Text>
</Box>
),
"First name": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
Faisal
</Text>
</Box>
),
"Last Name": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
Aljalahma
</Text>
</Box>
),
"Invested Amount": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
$100,000
</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 extractedArray = calcualtedData?.data?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Box
w={9}
display={"flex"}
alignItems={"center"}
isTruncated={true}
h={25}
>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{index + 1}
</Text>
</Box>
),
"Client Id": (
<Box w={100} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.clientId}
</Text>
</Box>
),
"First name": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.firstName}
</Text>
</Box>
),
"Last Name": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.lastName}
</Text>
</Box>
),
Amount: (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.amount?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</Box>
),
"Holding (%)": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.investor_holidings?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}%
</Text>
</Box>
),
"Exit Amt($)": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.distribution_amt?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</Box>
),
}));
const Total = () => {
return (
<Table size="sm">
<Tbody backgroundColor="gray.50">
<Tr>
<Tbody>
<Tr backgroundColor="gray.50">
<Th
textAlign={"left"}
p={3}
@@ -142,7 +238,7 @@ const Exit = ({ isOpen, onClose }) => {
<Th
textAlign={"center"}
p={3}
width="100px"
width="110px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
@@ -153,7 +249,7 @@ const Exit = ({ isOpen, onClose }) => {
<Th
textAlign={"center"}
p={3}
width="90px"
width="110px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
@@ -164,7 +260,7 @@ const Exit = ({ isOpen, onClose }) => {
<Th
textAlign={"center"}
p={3}
width="90px"
width="110px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
@@ -175,13 +271,16 @@ const Exit = ({ isOpen, onClose }) => {
<Th
textAlign={"left"}
p={3}
width="100px"
width="110px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
$1,000,000
{calcualtedData?.totalInvestedAmt?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Th>
<Th
textAlign={"left"}
@@ -191,8 +290,9 @@ const Exit = ({ isOpen, onClose }) => {
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
opacity={0}
>
100.0%
{calcualtedData?.distributed_per?.toFixed(2)}%
</Th>
<Th
textAlign={"center"}
@@ -203,7 +303,10 @@ const Exit = ({ isOpen, onClose }) => {
wordBreak="normal"
overflowWrap="normal"
>
$1,229,750
{calcualtedData?.distributed_amt?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Th>
</Tr>
</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 (
<Modal size={"xl"} isOpen={isOpen} onClose={onClose}>
<Modal size={"xl"} isOpen={isOpen} onClose={handleClose}>
<ModalOverlay />
<ModalContent maxW={1000}>
<ModalHeader fontSize={"md"}>Exit Transaction</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text as="label" mb='5px' fontSize='sm' fontWeight={500}>Enter Exit Amount</Text>
<HStack mb={4}>
<Input focusBorderColor="forestGreen.400" placeholder="$00.00" size={"sm"} className="col" />
<Button
size={"sm"}
rounded={"sm"}
colorScheme="forestGreen"
{/* <Text as="label" mb="5px" fontSize="sm" fontWeight={500}>
Amount to Distribute
</Text> */}
<HStack onSubmit={handleSubmit(onSubmit)} as={"form"} mb={4} alignItems={'center'}>
{/* <Input placeholder="$00.00" size={"sm"} className="col" /> */}
{/* <FormControl isInvalid={errors.amount} isRequired>*/}
<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>
<DataTable
{/* {calcualtedData && ( */}
<NormalData
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
setData={setExtractedArray}
caption={<Total />}
// total={<Total />}
// isLoading={isLoading}
/>
{/* ) } */}
</ModalBody>
<ModalFooter pt={0}>
<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>
{isCalcualtedData ? (
<>
<Button
bg={"hsla(139, 100%, 14%, 1)"}
mr={3}
color={"#fff"}
_hover={{
bg: "hsl(139deg 98.99% 26.59%)",
}}
size={"sm"}
rounded={"sm"}
onClick={() => onFinalSubmit()}
isLoading={isFinalCalculateLoading}
>
Save
</Button>
<Button size={"sm"} rounded={"sm"} mr={3} onClick={onClose}>
Close
</Button>
</>
) : (
""
)}
</ModalFooter>
</ModalContent>
</Modal>

View File

@@ -2,6 +2,7 @@ import {
Box,
Button,
FormControl,
FormErrorMessage,
FormLabel,
Input,
Modal,
@@ -11,52 +12,168 @@ import {
ModalFooter,
ModalHeader,
ModalOverlay,
Select,
Stack,
Text,
Textarea,
useToast,
} 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 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 (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalContent as={'form'} onSubmit={handleSubmit(onSubmit)} >
<ModalHeader fontSize={'md'}>Update iO NAV Transaction</ModalHeader>
<ModalCloseButton />
<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"} >
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>Amount</FormLabel>
<Input
size="sm"
rounded={'sm'}
textAlign={'end'}
focusBorderColor="forestGreen.300"
fontSize={"sm"} placeholder="$00.00" />
</FormControl>
<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 mb={"15px"}>
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
Comments
</FormLabel>
<Textarea
size="sm"
rounded={'sm'}
focusBorderColor="forestGreen.300"
fontSize={"sm"} placeholder="Write Comments" />
</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"}
>
{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>
<ModalFooter>
<Button
@@ -68,10 +185,12 @@ const UpdateIONav = ({ isOpen, onClose }) => {
}}
size={'sm'}
rounded={"sm"}
type="submit"
isLoading={isLoading}
>
Save
</Button>
<Button
<Button
size={'sm'}
rounded={"sm"} mr={3} onClick={onClose}>
Close

View File

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

View File

@@ -15,6 +15,7 @@ import {
Tag,
Text,
Tooltip,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
@@ -37,44 +38,79 @@ import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import ToastBox from "../../../Components/ToastBox";
import { useGetIOsQuery } from "../../../Services/io.service";
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";
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 navigate = useNavigate();
const toast = useToast();
const { IODetails, setIODetails, slideFromRight } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [statusFilter, setStatusFilter] = useState("all");
// const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
// ===============================[ Paginations ]
const { isOpen: isOpen, onOpen: onOpen, onClose: onClose } = useDisclosure();
// ===============================[ Paginations ]
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
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);
// ===============================[ Table Header ]
const tableHeadRow = [
"Sr No.",
"IO ID",
"IO Name",
"Sponsorer",
"Sponsor",
"Investment Type",
"Goal Amount",
"Holding Period",
"Amount Raised",
"Closing Date",
"IO Status",
// "Preview",
"Action",
];
@@ -94,9 +130,19 @@ const ViewIOTable = () => {
return nameMatches && statusMatches;
});
console.log(filteredData);
const extractedArray = filteredData?.map((item, index) => ({
const extractedArray = data?.data?.rows?.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>
),
"IO ID": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
@@ -105,13 +151,31 @@ const ViewIOTable = () => {
</Box>
),
"IO Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
<Tooltip
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 : "---"}
</Text>
</Box>
</Tooltip>
),
Sponsorer: (
Sponsor: (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.sponsor?.sponsorName ? item.sponsor?.sponsorName : "---"}
@@ -119,86 +183,139 @@ const ViewIOTable = () => {
</Box>
),
"Investment Type": (
<Box w={"auto"} isTruncated={true}>
<Box w={"120px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.investmentType?.investmentTypeName ? item.investmentType?.investmentTypeName : "---"}
{item?.investmentType?.investmentTypeName
? item.investmentType?.investmentTypeName
: "---"}
</Text>
</Box>
),
"Goal Amount": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item.goalAmount ? item.goalAmount : "---"}
<Box w={"100%"} display={"flex"} justifyContent={"start"} alignItems={'center'}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{`${parseFloat(item.goalAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text>
</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}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item.holdingPeriod ? item.holdingPeriod : "---"}
{/* {item.closingDate ? item.closingDate : "---"} */}
{formatDate(item.closingDate ? item.closingDate : "---")}
</Text>
</Box>
),
"IO Status": (
<Box w={"auto"} isTruncated={true}>
<Badge
rounded={"sm"}
pt={0.5}
pb={0.5}
rounded={"full"}
pt={1}
pb={1}
ps={4}
pe={4}
mt={1.5}
mb={1.5}
textTransform={"none"}
color={
item?.ioStatus?.statusAdmin === "Draft"
? "yellow.500"
: item.ioStatus === "Pending"
? "#6226EF"
: "#EF3826"
}
// variant={"solid"}
colorScheme={
item?.ioStatus?.statusAdmin === "Draft"
? "gray"
: item?.ioStatus?.statusAdmin === "Processing"
? "yellow"
: item.ioStatus?.statusAdmin === "Pending"
? "purple"
: "red"
: item?.ioStatus?.statusAdmin === "Open"
? "blue"
: 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}
</Badge>
</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: (
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Tooltip
{/* <Tooltip
rounded={"sm"}
fontSize={"xs"}
label="View"
bg="#fff"
color={"green.500"}
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
// _hover={{ color: "green.500" }}
colorScheme="green"
// transition={"0.5s all"}
onClick={() => {
navigate(`/view-io/${item.id}`);
}}
// color="green.300"
rounded={"sm"}
size={"xs"}
>
<ViewIcon />
</Button>
</Tooltip>
<ViewIcon me={2} /> View
</Button>
{/* </Tooltip> */}
<Tooltip
{/* <Tooltip
rounded={"sm"}
fontSize={"xs"}
label="View"
label="Edit"
bg="#fff"
color={"green.500"}
color={"blue.500"}
placement="top"
>
<Button
@@ -214,7 +331,7 @@ const ViewIOTable = () => {
>
<EditIcon />
</Button>
</Tooltip>
</Tooltip> */}
{/* <Tooltip
rounded={"sm"}
@@ -244,8 +361,6 @@ const ViewIOTable = () => {
const handleDelete = () => {};
console.log(extractedArray);
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<Box bg="white.500">
@@ -278,22 +393,27 @@ const ViewIOTable = () => {
cursor={"pointer"}
value={statusFilter} // Use the value prop here
>
<option value="all" selected disabled hidden defaultChecked>
<option value="" selected disabled hidden defaultChecked>
Status
</option>
<option value="all">All</option>
<option value="Draft">Draft</option>
<option value="Cancelled">Cancelled</option>
<option value="Processing">Processing</option>
<option value="Open">Open</option>
<option value="Exited">Exited</option>
<option value="Closed">Closed</option>
<option value="">All</option>
<option value="1">Draft</option>
<option value="6">Cancelled</option>
<option value="3">Processing</option>
<option value="2">Open</option>
<option value="5">Exited</option>
<option value="4">Closed</option>
</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"}>
<Button
@@ -325,6 +445,8 @@ const ViewIOTable = () => {
setMouseEntered={setMouseEntered}
/>
{/* <MobileView isOpen={isOpen} onClose={onClose} actionId={actionId} /> */}
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}

View File

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

View File

@@ -23,6 +23,7 @@ import {
Badge,
Box,
Icon,
HStack,
} from "@chakra-ui/react";
import header from "../../../assets/IOheader.png";
import { HiDotsVertical } from "react-icons/hi";
@@ -39,17 +40,16 @@ import Exit from "./HeaderModal/Exit";
import Cancle from "./HeaderModal/Cancle";
import { AddIcon } from "@chakra-ui/icons";
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 id = params?.id;
const { isOpen, onOpen, onClose } = useDisclosure();
const btnRef = useRef();
const { IODetails } = useContext(GlobalStateContext);
console.log(
"=================>>>>>",
IODetails?.artifactsImage?.[0]?.artifactPathName
);
const { IODetails, isIOloading } = useContext(GlobalStateContext);
const {
isOpen: isInvestmentOpen,
@@ -91,27 +91,103 @@ const ViewIOdataHeader = () => {
onOpen: onCancleOpen,
onClose: onCancleClose,
} = useDisclosure();
const bg = {
bg: "#fff",
};
const hover = {
textDecoration: "underline",
background: "#fff",
};
const style = {
fontSize: "0.875rem",
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
display={"flex"}
alignItems={"center"}
justifyContent={"start"}
justifyContent={"space-between"}
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"}
// bgGradient='linear(to-r, #caf5d8, #f5e8ca)'
// bgGradient='linear(to-r, #caf5d8, #d4a5a5)'
@@ -120,127 +196,244 @@ const ViewIOdataHeader = () => {
// bgGradient='linear(to-r, #ffd54f, #caf5d8)'
// bgGradient='linear(to-r, #caf5d8, #a8e6cf)'
boxShadow={"md"}
paddingRight={"10px"}
borderRadius={"10px"}
position={"relative"}
>
<Box h={100} w={200} p={1.5}>
{/* <Image rounded={'md'} h={"100%"} src={ " https://tanami.betadelivery.com/" + IODetails?.ioName} alt={IODetails?.ioName}/> */}
{IODetails?.artifactsImage?.[0]?.artifactPathName ? (
<Image
rounded={"md"}
h={"100%"}
w={"100%"}
objectFit={"cover"}
src={
" https://tanami.betadelivery.com/" +
IODetails?.artifactsImage?.[0]?.artifactPathName
}
alt={IODetails?.ioName}
/>
) : (
<Box
w={"100%"}
h={"100%"}
display={"flex"}
justifyContent={"center"}
alignItems={"center"}
bg={"#fff"}
rounded={"md"}
>
<Icon color={"gray.700"} as={GrGallery} />
<HStack gap={4}>
<Box h={100} w={200} p={1.5}>
{/* <Image rounded={'md'} h={"100%"} src={ " https://tanami.betadelivery.com/" + IODetails?.ioName} alt={IODetails?.ioName}/> */}
{IODetails?.artifactsImage?.[0]?.artifactPathName ? (
<Image
rounded={"md"}
h={"100%"}
w={"100%"}
objectFit={"cover"}
src={
import.meta.env.VITE_IMAGE_URL +
IODetails?.artifactsImage?.[0]?.artifactPathName
}
alt={IODetails?.ioName}
/>
) : (
<Box
w={"100%"}
h={"100%"}
display={"flex"}
justifyContent={"center"}
alignItems={"center"}
bg={"#fff"}
rounded={"md"}
>
<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 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}>
<Text as={"span"} fontSize={"xs"} color={"gray.500"} fontWeight={"500"}>
IO ID
</Text>
<Text as={"span"} fontSize={"sm"} fontWeight={"500"}>
{IODetails?.io_id ? IODetails?.io_id : "---"}
</Text>
</Box> */}
<Box display={"flex"} gap={2} pb={1}>
<Text
as={"span"}
fontSize={"xs"}
color={"gray.500"}
fontWeight={"500"}
me={2}
>
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}>
<Text as={"span"} fontSize={"xs"} color={"gray.500"} fontWeight={"500"}>
IO Name
</Text>
<Text as={"span"} fontSize={"sm"} fontWeight={"500"}>
{IODetails?.investmentNameEnglish
? IODetails?.investmentNameEnglish
: "---"}
</Text>
<Box display={"flex"} gap={2} pb={1}>
<Text
as={"span"}
fontSize={"xs"}
color={"gray.500"}
fontWeight={"500"}
me={2}
>
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 display={"flex"} flexDirection={"column"} gap={2}>
<Text as={"span"} fontSize={"xs"} color={"gray.500"} fontWeight={"500"}>
Sponsorer Name
</Text>
<Text as={"span"} fontSize={"sm"} fontWeight={"500"}>
{IODetails?.sponsor?.sponsorName
? IODetails?.sponsor?.sponsorName
: "---"}
</Text>
</Box>
<Box gap={8} me={12} w={"180px"}>
<Box display={"flex"} justifyContent={"space-between"} gap={2} pb={1}>
<Text
as={"span"}
textAlign={"center"}
fontSize={"xs"}
color={"gray.500"}
fontWeight={"500"}
>
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}>
<Text as={"span"} fontSize={"xs"} color={"gray.500"} fontWeight={"500"}>
IO Status
</Text>
<Badge
rounded={"sm"}
pt={0.5}
pb={0.5}
ps={4}
pe={4}
textTransform={"none"}
variant={"solid"}
color={"#fff"}
colorScheme={
IODetails?.ioStatus?.statusAdmin === "Draft"
? "blue"
: IODetails?.ioStatus?.statusAdmin === "Pending"
? "purple"
: "forestGreen"
}
>
{IODetails?.ioStatus?.statusAdmin
? IODetails?.ioStatus?.statusAdmin
: "---"}
</Badge>
</Box>
<Box display={"flex"} flexDirection={"column"} gap={2}>
<Text as={"span"} fontSize={"xs"} color={"gray.500"} fontWeight={"500"}>
IO NAV
</Text>
<Text as={"span"} fontSize={"sm"} fontWeight={"500"}>
{IODetails?.currentValuation ? IODetails?.currentValuation : "00.00"}
</Text>
</Box>
<Box display={"flex"} flexDirection={"column"} gap={2}>
<Text as={"span"} fontSize={"xs"} color={"gray.500"} fontWeight={"500"}>
IO cash
</Text>
<Text as={"span"} fontSize={"sm"} fontWeight={"500"}>
{IODetails?.ioCash ? IODetails?.ioCash : "00.00"}
</Text>
</Box>
<Box display={"flex"} flexDirection={"column"} gap={2}>
<Text as={"span"} fontSize={"xs"} color={"gray.500"} fontWeight={"500"}>
IO MV NAV
</Text>
<Text as={"span"} fontSize={"sm"} fontWeight={"500"}>
{IODetails?.marketValue ? IODetails?.marketValue : "00.00"}
</Text>
<Box display={"flex"} justifyContent={"space-between"} gap={2} pb={1}>
<Text
as={"span"}
fontSize={"xs"}
color={"gray.500"}
fontWeight={"500"}
>
IO MV :-
</Text>
<Text as={"span"} fontSize={"xs"} fontWeight={"500"}>
{/* {IODetails?.ioNAV ? formatCurrency(removeTrailingZeros(IODetails?.ioNAV)) : "00.00"} */}
{parseFloat(IODetails?.ioNAV || 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"}
>
IO cash :-
</Text>
<Text as={"span"} fontSize={"xs"} fontWeight={"500"}>
{/* {IODetails?.ioCash ? formatCurrency(removeTrailingZeros(IODetails?.ioCash)) : "00.00"} */}
{parseFloat(IODetails?.ioCash || 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"}
>
IO NAV :-
</Text>
<Text as={"span"} fontSize={"xs"} fontWeight={"500"}>
{/* {IODetails?.ioMVNAV ? formatCurrency(removeTrailingZeros(IODetails?.ioMVNAV)) : "00.00"} */}
{parseFloat(IODetails?.ioMVNAV || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</Box>
</Box>
<Box
@@ -272,114 +465,19 @@ const ViewIOdataHeader = () => {
>
Tansaction
</MenuItem>
<MenuItem onClick={onInvestmentOpen} className="border-bottom">
Amount Invested
</MenuItem>
<MenuItem onClick={onFeesOpen} className="border-bottom">
Fees & Expenses
</MenuItem>
<MenuItem onClick={onDistSponsorOpen} className="border-bottom">
Distribution from Sponsors
</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>
{filteredMenu?.map(({ id, title, onClickFunction }) => (
<MenuItem
key={id}
onClick={onClickFunction}
className="border-bottom"
>
{title}
</MenuItem>
))}
</MenuList>
</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 */}
<AmountInvested isOpen={isInvestmentOpen} onClose={onInvestmentClose} />
<FeesExpenses isOpen={isFeesOpen} onClose={onFeesClose} />
@@ -393,13 +491,34 @@ const ViewIOdataHeader = () => {
isOpen={isDistInvestorOpen}
onClose={onDistInvestorClose}
/>
<UpdateIONav isOpen={isUpdateNavOpen} onClose={onUpdateNavClose} />
<UpdateIOStatus
status={IODetails?.nextStatus}
isOpen={isUpdateStatusOpen}
onClose={onUpdateStatusClose}
/>
</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 { yupResolver } from "@hookform/resolvers/yup";
import { formatDate } from "../../Master/Sponser/Sponsers";
import { formatCurrency } from "../../../Components/CurrencyInput";
import { removeTrailingZeros } from "../../../Constants/Constants";
const schema = yup.object().shape({
investmentNameEnglish: yup
@@ -71,7 +73,7 @@ const schema = yup.object().shape({
.positive("Expected return must be a positive number")
.min(0.01, "Expected return must be at least 0.01"),
});
const ViewIOdetails = () => {
const navigate = useNavigate();
const params = useParams();
@@ -104,31 +106,33 @@ const ViewIOdetails = () => {
investmentNameArabic: IObyID?.data?.investmentNameArabic,
descriptionEnglish: IObyID?.data?.descriptionEnglish,
descriptionArabic: IObyID?.data?.descriptionArabic,
goalAmount: IObyID?.data?.goalAmount,
goalAmount: removeTrailingZeros(IObyID?.data?.goalAmount),
closingDate: IObyID?.data?.closingDate,
holdingPeriod: IObyID?.data?.holdingPeriod,
holdingPeriod: IObyID?.dpata?.holdingPeriod,
ISIN: IObyID?.data?.ISIN,
comment: IObyID?.data?.comment,
expectedReturn: IObyID?.data?.expectedReturn,
investmentType_xid: IObyID?.data?.investmentType_xid,
investmentType_xid: IObyID?.data?.investmentType_xid,
InvestmentDetails: IObyID?.data?.InvestmentDetails,
minInvestmentAmount: IObyID?.data?.minInvestmentAmount,
});
}
}, [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{
id:id,
country: country?.countryName,
value: minInvestmentAmt,
value: removeTrailingZeros(minInvestmentAmt),
logo: country?.flagIcon,
curr: country?.countryCode,
curr: currencyCode,
}
})
console.log();
//=======================[ Editor ]
const formFields = [
{
@@ -167,7 +171,7 @@ const ViewIOdetails = () => {
},
{
label: "Description (Arabic)",
name: "descriptionArabic",
name: "descriptionArabic",
value: IObyID?.data?.descriptionArabic
? IObyID?.data?.descriptionArabic
: "---",
@@ -177,6 +181,70 @@ const ViewIOdetails = () => {
section: " ",
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",
placeHolder: "Select option",
@@ -190,7 +258,7 @@ const ViewIOdetails = () => {
width: "32.3%",
},
{
label: "Sponsorer Name",
label: "Sponsor Name",
placeHolder: "Select option",
name: "sponsor_xid",
type: "select",
@@ -204,10 +272,12 @@ const ViewIOdetails = () => {
{
label: "Goal Amount",
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",
type: "number",
isRequired: true,
arabic:true,
section: " ",
width: "32.3%",
},
@@ -222,28 +292,6 @@ const ViewIOdetails = () => {
section: " ",
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",
placeHolder: "$00.00",
@@ -274,7 +322,6 @@ const ViewIOdetails = () => {
section: " ",
width: "100%",
isRequired: true,
type: "table",
value: minInvestmentById,
},
@@ -299,7 +346,7 @@ const ViewIOdetails = () => {
}, {});
if (!IObyID?.data) {
return <FullscreenLoaders />;
return <FullscreenLoaders height={'70vh'} />;
}
return (
@@ -314,10 +361,10 @@ const ViewIOdetails = () => {
colorScheme="forestGreen"
rounded={"sm"}
size={"xs"}
>
>
Edit IO
</Button>{" "}
<FormInputView groupedFields={groupedFields} />{" "}
</Button>
<FormInputView groupedFields={groupedFields} />
</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,
HStack,
Input,
Menu,
MenuButton,
MenuItem,
MenuList,
Portal,
Select,
Switch,
Tag,
@@ -21,13 +16,8 @@ import {
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 { Link, Link as RouterLink, useNavigate } from "react-router-dom";
import {
AddIcon,
DeleteIcon,
EditIcon,
EmailIcon,
ViewIcon,
} from "@chakra-ui/icons";
import Pagination from "../../../Components/Pagination";
@@ -38,6 +28,8 @@ 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, exportToExcelNew, generateSerialNumber } from "../../../Constants/Constants";
import { LuFileSpreadsheet } from "react-icons/lu";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
@@ -47,7 +39,6 @@ const InvestorDetails = () => {
const thirdField = useRef();
const { InvestorDetails, setInvestorDetails, slideFromRight } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
@@ -60,15 +51,44 @@ const InvestorDetails = () => {
} = 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("");
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: investorDetails,
isLoading: investorDetailsLoading,
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(() => {
// Simulate loading
@@ -82,61 +102,48 @@ const InvestorDetails = () => {
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr N/O",
"Sr No",
"Client ID",
"First Name",
"Last Name",
"Country",
"Country",
"Phone Number",
"E-mail ID",
"Investor Type",
"Type",
"KYC Status",
"Status",
"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]================================================================
const filteredData = investorDetails?.data?.rows?.filter((item) => {
// Filter by name (case insensitive)
const name = item.clientReference_id;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name?.toLowerCase().includes(searchLower);
const exportInvestor = investorDetails?.data?.rows?.map((item, idx) => ({
"Id": parseInt(item?.id, 10) || item?.id, // Convert to integer, fallback to string if conversion fails
"Client ID": item?.clientReference_id, // This is likely a string
"First Name": item?.principal?.firstName,
"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 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) => ({
const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({
id: item?.id,
"Sr N/O": (
"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}
{/* {item.id} */}
{generateSerialNumber(idx,currentPage, pageSize )}
</Text>
),
"Client ID": (
@@ -144,7 +151,7 @@ const InvestorDetails = () => {
<Text as={"span"} color={"teal.900"}>
{item.clientReference_id}
</Text>
</Box>
</Box>
),
"First Name": (
<Box w={"auto"} isTruncated={true}>
@@ -154,7 +161,7 @@ const InvestorDetails = () => {
</Box>
),
"Last Name": (
<Box w={"auto"} isTruncated={true}>
<Box w={"70px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.principal?.lastName}
</Text>
@@ -181,10 +188,10 @@ const InvestorDetails = () => {
</Text>
</Box>
),
"Investor Type": (
"Type": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"purple"}>
<Badge colorScheme="purple" variant={'solid'} fontWeight={"500"} px={2} py={0.5}>
<Text as={"span"} >
<Badge color={"forestGreen.500"} variant={'ghost'} fontWeight={"700"} px={2} py={0.5}>
{item?.investor_type?.investorTypeName}
</Badge>
</Text>
@@ -193,29 +200,31 @@ const InvestorDetails = () => {
Status: (
<Box w={"auto"} isTruncated={true}>
<Badge
fontWeight={"500"}
// textTransform={"none"}
fontWeight={"700"}
textTransform={"none"}
colorScheme={item.ioStatus ? "red" : "green"}
px={2}
py={0.5}
variant={'solid'}
>
{item.ioStatus ? "Ban" : "Unban"}
</Badge>
</Box>
),
"KYC Status": (
<Box w={"auto"} isTruncated={true}>
<Badge
fontWeight={"500"}
// textTransform={"none"}
colorScheme={item.kycStatus ? "blue" : "red"}
<Box w={"auto"} display={'flex'} alignItems={'center'} isTruncated={true}>
<Text
as={'span'}
fontWeight={"700"}
textTransform={"none"}
color={item?.KYCStatus === true ? "green" : "yellow.500"}
px={2}
py={0.5}
variant={'solid'}
>
{item.KYCStatus ? "Completed" : "Not complete"}
</Badge>
{/* {item.KYCStatus ? "Completed" : "Not complete"} */}
{item?.KYCStatus === true ? "Completed" : "Not Completed"}
</Text>
</Box>
),
Action: (
@@ -229,6 +238,7 @@ const InvestorDetails = () => {
placement="top"
>
<Button
isDisabled={item.ioStatus}
onClick={() => {
navigate(`/investor-details/profile-view/${item.id}`);
}}
@@ -259,10 +269,6 @@ const InvestorDetails = () => {
setIsLoading(true);
};
const handleEdit = (id) => {
setActionId(id);
onEditOpen();
};
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
@@ -294,14 +300,16 @@ const InvestorDetails = () => {
size={"sm"}
fontSize={"xs"}
cursor={"pointer"}
onChange={(e) => setStatus(e.target.value)}
value={status}
>
<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>
<option value="">Status</option>
<option value="0">Ban</option>
<option value="1">UnBan</option>
</Select>
<Select
@@ -309,43 +317,15 @@ const InvestorDetails = () => {
size={"sm"}
fontSize={"xs"}
cursor={"pointer"}
onChange={(e) => setKyc(e.target.value)}
value={kyc}
>
<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>
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>
<option value="" defaultValue={""} disabled hidden>
KYC Status
</option>
<option value="all">All</option>
<option value="completed">Completed</option>
<option value="incompleted">Incompleted</option>
<option value="">KYC Status</option>
<option value="0">Incompleted</option>
<option value="1">Completed</option>
</Select>
<Select
@@ -353,26 +333,51 @@ const InvestorDetails = () => {
size={"sm"}
fontSize={"xs"}
cursor={"pointer"}
onChange={(e) => setCountry(e.target.value)}
value={country}
>
<option value="" defaultValue={"all"} disabled hidden>
<option value="" defaultValue={""} disabled hidden>
Country
</option>
<option value="behrain">Behrain</option>
<option value="kuwait">Kuwait</option>
<option value="oman">Oman</option>
<option value="saudi arabia">Saudi arabia</option>
<option value="united arab emirates">United arab emirates</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>
<Pagination
isLoading={investorDetailsLoading}
isLoading={investorDetailsLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={investorDetails?.data?.totalItems}
/>
</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>
<InvestmentDetailsEdit
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 {
Badge,
Box,
Divider,
Icon,
Tab,
TabList,
@@ -19,31 +20,47 @@ import FormInputView from "../../../Components/FormInputView";
import ViewInvestorDetails from "./ViewInvestorDetails";
import { LuWallet } from "react-icons/lu";
import Transaction from "./Transaction";
import FullscreenLoaders from '../../../Components/Loaders/FullscreenLoaders'
import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
import { useGetInvestorsDetailsByIdQuery } from "../../../Services/investor.details.service";
import BankDetails from "./BankDetails";
import Kyc from "./Kyc";
const ProfileView = () => {
const navigate = useNavigate();
const params = useParams();
const { InvestorDetails } = useContext(GlobalStateContext);
const {
InvestorDetails,
setViewInvestor,
setTransaction,
InvestorWallet,
setInvestorWallet,
} = useContext(GlobalStateContext);
const { reset } = useForm(); // assuming react-hook-form
const id = params?.id;
const { data, isLoading, errors } = useGetInvestorsDetailsByIdQuery(id, {
const { data, isLoading, errors, refetch} = useGetInvestorsDetailsByIdQuery(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 = [
{
label: "Client ID",
value: foundObject?.id,
value: foundObject?.clientReference_id,
type: "text",
isRequired: true,
section: "Personal Details",
@@ -51,7 +68,7 @@ const ProfileView = () => {
},
{
label: "First Name",
value: foundObject?.principal?.firstName,
value: foundObject?.firstName,
type: "text",
isRequired: true,
section: "Personal Details",
@@ -60,7 +77,7 @@ const ProfileView = () => {
{
label: "Last Name",
value: foundObject?.principal?.lastName,
value: foundObject?.lastName,
type: "text",
isRequired: true,
section: "Personal Details",
@@ -68,7 +85,7 @@ const ProfileView = () => {
},
{
label: "E-mail ID",
value: foundObject?.principal?.emailAddress,
value: foundObject?.emailAddress,
type: "text",
isRequired: true,
section: "Personal Details",
@@ -76,7 +93,7 @@ const ProfileView = () => {
},
{
label: "Phone Number",
value: foundObject?.principal?.mobileNumber,
value: foundObject?.mobileNumber,
type: "tel",
isRequired: true,
section: "Personal Details",
@@ -84,7 +101,7 @@ const ProfileView = () => {
},
{
label: "Country",
value: foundObject?.country?.countryName,
value: foundObject?.country ? foundObject?.country : "---",
type: "text",
isRequired: true,
section: "Personal Details",
@@ -92,23 +109,23 @@ const ProfileView = () => {
},
{
label: "Investment type",
value: foundObject?.investor_type?.investorTypeName,
type: "text",
isRequired: true,
section: "Personal Details",
width: "32%",
},
{
label: "Investor limit",
value: foundObject?.investorLimit,
value: foundObject?.investorTypeName,
type: "text",
isRequired: true,
section: "Personal Details",
width: "32%",
},
// {
// label: "Investor limit",
// value: foundObject?.investorLimit,
// type: "text",
// isRequired: true,
// section: "Personal Details",
// width: "32%",
// },
{
label: "KYC Status",
value: foundObject?.KYCStatus ? "Completed" : "Not complete",
value: foundObject?.KYCStatus ? "Completed" : "Not complete",
type: "text",
isRequired: true,
section: "Personal Details",
@@ -124,7 +141,7 @@ const ProfileView = () => {
},
{
label: "Default language",
value: foundObject?.defaultLanguage_xid === 1 ? "English" : "Arabic" ,
value: foundObject?.defaultLanguage_xid === 1 ? "English" : "Arabic",
type: "text",
isRequired: true,
section: "Personal Details",
@@ -132,12 +149,60 @@ const ProfileView = () => {
},
{
label: "App Notification",
value: foundObject?.IsAppNotificationEnabled === 1 ? "Yes" : "No" ,
value: foundObject?.IsAppNotificationEnabled === true ? "Yes" : "No",
type: "text",
isRequired: true,
section: "Personal Details",
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) => {
@@ -149,8 +214,9 @@ const ProfileView = () => {
return groups;
}, {});
return (
isLoading? <FullscreenLoaders />:
return isLoading ? (
<FullscreenLoaders />
) : (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={14}>
<Box
pt={2}
@@ -191,10 +257,10 @@ const ProfileView = () => {
display={"flex"}
>
<Text fontWeight={500} as={"span"}>
26,763.40
{parseFloat(InvestorWallet?.WalletBalance_InUSD || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</Text>
<Badge display={"flex"} alignItems={"center"}>
USD
{InvestorWallet?.currencyCode_USD}
</Badge>
</Box>
</Box>
@@ -211,10 +277,10 @@ const ProfileView = () => {
display={"flex"}
>
<Text fontWeight={500} as={"span"}>
10,075.01
{parseFloat(InvestorWallet?.WalletBalance_InInvCur || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</Text>
<Badge display={"flex"} alignItems={"center"}>
BHD
{InvestorWallet?.currencyCode_InCur}
</Badge>
</Box>
</Box>
@@ -238,6 +304,7 @@ const ProfileView = () => {
View Details
</Tab>
<Tab
// isDisabled={true}
fontSize={"sm"}
_selected={{
color: "#004118",
@@ -247,6 +314,7 @@ const ProfileView = () => {
Portfolio
</Tab>
<Tab
// isDisabled={true}
fontSize={"sm"}
_selected={{
color: "#004118",
@@ -256,19 +324,22 @@ const ProfileView = () => {
Transaction
</Tab>
<Tab
// isDisabled={true}
fontSize={"sm"}
_selected={{
color: "#004118",
borderBottom: "2px solid #38a169",
}}
>
documents
KYC
</Tab>
</TabList>
</Box>
<TabPanels>
<TabPanel>
<FormInputView width={"32%"} groupedFields={groupedFields} />
<Divider/>
<BankDetails />
</TabPanel>
<TabPanel>
<ViewInvestorDetails />
@@ -276,6 +347,9 @@ const ProfileView = () => {
<TabPanel>
<Transaction />
</TabPanel>
<TabPanel>
<Kyc />
</TabPanel>
</TabPanels>
</Tabs>
</Box>

View File

@@ -1,374 +1,312 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Menu,
MenuButton,
MenuItem,
MenuList,
Portal,
Select,
Switch,
Table,
Tag,
Tbody,
Text,
Th,
Tooltip,
Tr,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import DataTable from "../../../Components/DataTable/DataTable";
import { HiDotsVertical } from "react-icons/hi";
import { Link, Navigate, Link as RouterLink } from "react-router-dom";
import {
AddIcon,
DeleteIcon,
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);
};
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Menu,
MenuButton,
MenuItem,
MenuList,
Portal,
Select,
Switch,
Table,
Tag,
Tbody,
Text,
Th,
Tooltip,
Tr,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import NormalTable from "../../../Components/DataTable/NormalTable";
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 Total = () => {
return (
<Table size="sm">
<Tbody backgroundColor="gray.50">
<Tr >
<Th
textAlign={"center"}
p={3}
width="60px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
Balance:
</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="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>
);
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
const Transaction = () => {
const toast = useToast();
const { transaction, setTransaction, slideFromRight, InvestorWallet } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
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";
import React, { useContext, useEffect, useState } from "react";
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 { Link, Navigate, Link as RouterLink } from "react-router-dom";
import {
Link,
Navigate,
Link as RouterLink,
useParams,
} from "react-router-dom";
import {
AddIcon,
DeleteIcon,
@@ -34,12 +39,13 @@ import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import ToastBox from "../../../Components/ToastBox";
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 ViewInvestorDetails = () => {
const toast = useToast();
const { viewInvestor, setViewInvestor, slideFromRight } =
const { viewInvestor, setViewInvestor, slideFromRight, InvestorWallet } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
@@ -47,6 +53,16 @@ const ViewInvestorDetails = () => {
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
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(() => {
// Simulate loading
@@ -60,31 +76,27 @@ const ViewInvestorDetails = () => {
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr N/O",
"Deal Name",
// "Sr N/O",
// "Deal Name",
"IO Name",
"Sponsor Name",
"Investment Type",
"Investment Amount",
"Holding Period",
"Estimated return",
"Percentage",
"Market Value",
"Return on Investment",
"Distribution",
"Distribution Percent",
"Total return",
"Total return on Investment",
"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]================================================================
const filteredData = viewInvestor?.filter((item) => {
// Filter by name (case insensitive)
const name = item.dealId;
const name = item.sponsorName;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name?.toLowerCase().includes(searchLower);
@@ -100,22 +112,23 @@ const ViewInvestorDetails = () => {
return nameMatches;
});
const extractedArray = filteredData?.map((item) => ({
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id,
"Sr N/O": (
<Text w={'50px'}
<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}
{index + 1}.
</Text>
),
"Deal Name": (
"IO Name": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.dealName}
{item.investmentNameEnglish}
</Text>
</Box>
),
@@ -126,45 +139,205 @@ const ViewInvestorDetails = () => {
</Text>
</Box>
),
"Investment Type": (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.investmentTypeName}
</Text>
</Box>
),
"Investment Amount": (
<Box w={"auto"} isTruncated={true}>
<Box
display={"flex"}
justifyContent={isSwitchOn ? "end" : "start"}
w={"auto"}
isTruncated={true}
>
<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>
</Box>
),
"Holding Period": (
Percentage: (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.holdingPeriod}
{item?.Investor_Holidings} %
</Text>
</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}>
<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>
</Box>
),
Status: (
<Box w={"auto"} isTruncated={true}>
<Badge
fontWeight={"500"}
textTransform={"none"}
color={
item.kycStatus === "Open"
? "green"
: item.kycStatus === "Pending"
? "blue"
: "red"
}
px={2}
py={0.5}
>
{item.kycStatus}
</Badge>
</Box>
<Badge
rounded={"full"}
pt={1}
pb={1}
ps={4}
pe={4}
mt={1.5}
mb={1.5}
textTransform={"none"}
// variant={"solid"}
colorScheme={
item?.statusAdmin === "Draft"
? "gray"
: item?.statusAdmin === "Processing"
? "yellow"
: item?.statusAdmin === "Open"
? "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: (
<Box display={"flex"} justifyContent={"space-between"}>
@@ -203,6 +376,11 @@ const ViewInvestorDetails = () => {
setIsLoading(true);
};
const switchOnChangeHandle = () => {
setIsSwitchOn(!isSwitchOn); // Toggle the switch state
setViewInvestor(data?.data?.portfolio);
};
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<Box bg="white.500">
@@ -225,14 +403,54 @@ const ViewInvestorDetails = () => {
value={searchTerm}
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} />
</HStack>
</HStack> */}
</HStack>
</Box>
<DataTable
<NormalTable
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
@@ -250,7 +468,6 @@ const ViewInvestorDetails = () => {
alertHandler={handleDelete}
isLoading={isLoading}
/>
</Box>
);
};

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