Compare commits

...

378 Commits

Author SHA1 Message Date
Swapnil Bendal
f2d8aee6a9 Refactor DeletionRequestApprove component to include refetch functionality and improve error handling 2025-01-20 20:13:30 +05:30
YasinShaikh123
a108dcdb17 Merge branch 'sprint10/reversal' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into sprint10/reversal 2025-01-20 19:36:56 +05:30
YasinShaikh123
41a1bde62b [ deletion request modal bugs ] 2025-01-20 19:35:58 +05:30
Swapnil Bendal
efddfe6d7a Fix data extraction in DeletionRequest component to use correct data structure 2025-01-20 18:40:20 +05:30
Swapnil Bendal
abe9b14436 Add validation mode to form and adjust comment input margin 2025-01-20 18:07:09 +05:30
Swapnil Bendal
f31b67f676 Enhance comment validation in deletion request approval and refactor code for clarity 2025-01-20 18:04:55 +05:30
YasinShaikh123
066e2fc169 [ bud update 🔥 ] 2025-01-20 17:26:42 +05:30
YasinShaikh123
2892334e41 [ bug fixing done] 2025-01-20 16:56:31 +05:30
YasinShaikh123
103959b40b Merge branch 'sprint10/reversal' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into sprint10/reversal 2025-01-20 15:06:33 +05:30
YasinShaikh123
714804fdd6 [ Bug Fixing 👍 ] 2025-01-20 15:05:39 +05:30
satyam70288
af2485ba58 Fix minimum length message for comment validation in reversal popups 2025-01-18 11:30:54 +00:00
satyam70288
2b89e52e58 Remove number validation from Sponsor Name in confirmation and rejection popups 2025-01-18 11:28:42 +00:00
Swapnil Bendal
05f1d4055e Refactor API queries to include pagination and search parameters in bank deposit and account deletion services 2025-01-17 19:57:48 +05:30
Swapnil Bendal
efbb72ca4c Refactor validation schema by renaming 'comments' to 'comment' in ConfirmReversalPopups and RejectReversalPopups components 2025-01-17 19:16:10 +05:30
Swapnil Bendal
fba6a7ad45 Refactor validation schema by renaming 'comments' to 'comment' in InitiateReversalPopup component 2025-01-17 19:08:29 +05:30
Swapnil Bendal
0ccfba1238 Fix validation schema by renaming 'comments' to 'comment' in InitiateReversalPopups component 2025-01-17 18:57:10 +05:30
Swapnil Bendal
5e86a72700 Filter navigation items based on user role in DashboardLayout component 2025-01-17 18:29:10 +05:30
YasinShaikh123
9f93993938 [ update approve modal ] 2025-01-17 17:51:58 +05:30
YasinShaikh123
79c822bb4d [ bankdeposite aprove ] 2025-01-17 17:45:51 +05:30
Swapnil Bendal
7523fd48f2 Implement account deletion reversal functionality in DeletionHistory component 2025-01-17 17:09:34 +05:30
YasinShaikh123
7e8f5e1115 [ kaam chalu hai👷‍♂️ ] 2025-01-17 17:01:49 +05:30
YasinShaikh123
ccd27b7840 [update]- fawateerdeposite 2025-01-17 16:34:46 +05:30
Swapnil Bendal
27b7e31930 Conditionally display "Reversal Action" column in DeletionHistory based on user role 2025-01-17 16:22:01 +05:30
Swapnil Bendal
27d243ee30 Merge branch 'sprint10/swapnil/reversal' into sprint10/reversal 2025-01-17 16:19:48 +05:30
Swapnil Bendal
ba06bf28c0 Add account deletion reversal functionality and update DeletionHistory component 2025-01-17 16:19:35 +05:30
YasinShaikh123
cbfeff4710 [ Update table fawateer deposite ] 2025-01-17 16:02:01 +05:30
Swapnil Bendal
c82ef27958 Add mutation endpoints for Fawateer reversal requests and update DepositHistory component 2025-01-17 15:19:48 +05:30
Swapnil Bendal
067edfb4a5 Enhance InitiateReversalPopup and DepositHistory: add loading state and integrate reversal request mutation 2025-01-17 14:30:22 +05:30
Swapnil Bendal
92977c4bed Merge branch 'sprint10/reversal' into sprint10/swapnil/reversal 2025-01-17 13:29:26 +05:30
Swapnil Bendal
c7856baf45 Update DepositHistory component: rename "Reversal" to "Reversal Action" and clean up commented code 2025-01-17 13:28:59 +05:30
YasinShaikh123
27d3167acc update bank service 2025-01-17 13:28:45 +05:30
Swapnil Bendal
9b5dd51660 Merge branch 'sprint10/reversal' into sprint10/swapnil/reversal 2025-01-17 13:21:21 +05:30
Swapnil Bendal
7f3dd5aa79 Add InitiateReversalPopup component and integrate into DepositHistory 2025-01-17 13:15:25 +05:30
YasinShaikh123
584df9b140 Merge branch 'sprint10/reversal' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into sprint10/reversal 2025-01-17 13:14:01 +05:30
YasinShaikh123
31d237b675 [ update reversal service ] 2025-01-17 13:13:57 +05:30
Swapnil Bendal
3f5e154e23 Merge branch 'Sprint-10' into sprint10/reversal 2025-01-17 10:28:48 +05:30
satyam70288
bb9513fcfe Merge branch 'sprint10/dashbaord' into Sprint-10 2025-01-16 10:28:17 +00:00
YasinShaikh123
d196dffd12 change bugs 2025-01-16 15:57:18 +05:30
YasinShaikh123
f9b1f820c2 [working Reversal] 2025-01-16 15:55:42 +05:30
satyam70288
6d2964036a Merge branch 'sprint10/dashbaord' into Sprint-10 2025-01-16 10:20:07 +00:00
YasinShaikh123
21eb9e41ff [fixed] - spell correct 2025-01-16 15:47:22 +05:30
Swapnil Bendal
87f8977c73 Merge branch 'sprint10/dashbaord' into Sprint-10 2025-01-15 20:01:35 +05:30
YasinShaikh123
390ec27e35 update icon size 2025-01-15 19:59:53 +05:30
Swapnil Bendal
5ff437970e Merge branch 'sprint10/dashbaord' into Sprint-10 2025-01-15 19:57:40 +05:30
YasinShaikh123
1beeeed8d3 [ Dashboard design changes done 😎😷 ] 2025-01-15 19:53:02 +05:30
YasinShaikh123
401c9daf75 Merge branch 'sprint10/dashbaord' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into sprint10/dashbaord 2025-01-15 19:02:46 +05:30
YasinShaikh123
d23bf5d7e9 [ Change Icon 🥱] 2025-01-15 19:02:43 +05:30
Swapnil Bendal
20213408c4 [update] - enforce message requirement for specific status in UpdateIOStatus component 2025-01-15 18:00:54 +05:30
Swapnil Bendal
3d456c3c48 [update] - refactor Notification component for improved readability and adjust message validation 2025-01-15 17:55:59 +05:30
Swapnil Bendal
4e4de8caf5 [update] - format percentage display and adjust navigation path 2025-01-15 17:29:17 +05:30
Swapnil Bendal
150194cc26 Merge branch 'Sprint-10' into sprint10/dashbaord 2025-01-15 17:07:18 +05:30
YasinShaikh123
5928af4283 [update] - dashboard ❤️😒 2025-01-15 17:06:00 +05:30
YasinShaikh123
55a397594e Merge branch 'Sprint-10' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into Sprint-10 2025-01-13 19:24:06 +05:30
YasinShaikh123
874940ae7c Spelling 2025-01-13 19:24:03 +05:30
YasinShaikh123
96a302d262 Dashboard Working👷‍♂️ 2025-01-13 19:09:57 +05:30
Swapnil Bendal
18035047d4 Merge remote-tracking branch 'origin/dev' into Sprint-10 2025-01-10 19:00:54 +05:30
Swapnil Bendal
a51585089c refactor: simplify exportInvestor mapping and currency formatting 2025-01-10 17:42:20 +05:30
YasinShaikh123
ebcb06bf5e hogaya 2025-01-10 17:34:09 +05:30
YasinShaikh123
84dc47b447 done changes and bug 2025-01-10 17:28:26 +05:30
YasinShaikh123
aeb0f25663 kaam chalu hai😉 2025-01-10 14:53:55 +05:30
YasinShaikh123
a07d011c85 [Distribution Amt] 2025-01-09 20:15:06 +05:30
YasinShaikh123
0ed01bf94f [fixed] - model 2025-01-09 19:33:54 +05:30
YasinShaikh123
96f8c32483 working 2025-01-09 19:31:52 +05:30
YasinShaikh123
45f69fe2b7 update KYC 2025-01-09 17:53:51 +05:30
YasinShaikh123
57c6923784 [dashboard working👷‍♂️] 2025-01-09 17:40:55 +05:30
Swapnil Bendal
974d1501b2 [update] - Hard code changes 2025-01-08 20:57:53 +05:30
YasinShaikh123
c33e358e8e change investor ammont with 2025-01-08 20:38:23 +05:30
Swapnil Bendal
1434088c1b [update] - spell check 2025-01-08 20:31:58 +05:30
YasinShaikh123
3c6f083432 update error message 2025-01-08 20:08:57 +05:30
YasinShaikh123
f81b210b0a upadate investor and notifation👍 2025-01-08 19:51:35 +05:30
YasinShaikh123
5743cadf5e notafication dropdown 2025-01-08 17:04:07 +05:30
Swapnil Bendal
4579573f23 Merge branch 'Sprint-10' into dev 2025-01-08 15:43:22 +05:30
YasinShaikh123
625f721325 correct exchange rate 2025-01-08 13:16:44 +05:30
YasinShaikh123
01aece9bf6 update 2025-01-07 18:46:00 +05:30
YasinShaikh123
d9692c3890 update exchange rate numbar 2025-01-07 15:55:38 +05:30
Swapnil
6e42bc0adb [FIXED] - notification 2025-01-04 21:05:49 +05:30
YasinShaikh123
c9e5223989 Case IO Bug 2024-12-31 19:34:36 +05:30
YasinShaikh123
69f76bbdce [fixed] - closing date 2024-12-31 16:06:13 +05:30
YasinShaikh123
25df0d6160 [fixed] - io Deatils 2024-12-31 15:47:48 +05:30
YasinShaikh123
51727d4de1 [test] 2024-12-31 13:48:50 +05:30
YasinShaikh123
1539493641 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-12-31 13:45:31 +05:30
YasinShaikh123
d63ac2eb2b bugs fix 2024-12-31 13:42:28 +05:30
Swapnil Bendal
9eca3ae9fc [update] - pagination on investor details 2024-12-27 15:22:34 +05:30
Swapnil Bendal
212f5d4d37 [fixed] - on IO Transaction 2024-12-24 18:08:15 +05:30
YasinShaikh123
b620cd410d update bugs👍 2024-12-24 17:56:55 +05:30
Swapnil Bendal
84298ff453 [update] - IO Transaction bug fix 2024-12-24 16:59:01 +05:30
Swapnil Bendal
8eae4222f4 [fixed] - pending action 2024-12-24 16:36:07 +05:30
52a987b616 Merge pull request '[fixed] - token service' (#28) from dev into main
Reviewed-on: #28
2024-12-23 14:48:38 +00:00
Swapnil Bendal
2a3c211b56 [fixed] - token service 2024-12-23 19:33:08 +05:30
4d6a8bb472 Merge pull request 'dev' (#27) from dev into main
Reviewed-on: #27
2024-12-20 14:49:35 +00:00
Swapnil Bendal
41a60c0892 [fixed] - change password 2024-12-20 20:18:26 +05:30
Swapnil Bendal
5c05a68bb0 [update] - readme 2024-12-20 20:10:43 +05:30
f02f9c8b7d Merge pull request '[update] - readme' (#26) from dev into main
Reviewed-on: #26
2024-12-20 14:39:43 +00:00
Swapnil Bendal
137912aa11 [update] - readme 2024-12-20 20:08:44 +05:30
e3fe5a1618 Merge pull request '[fixed] - changes password' (#25) from dev into main
Reviewed-on: #25
2024-12-20 14:30:54 +00:00
Swapnil Bendal
edcb4cd7b9 [fixed] - changes password 2024-12-20 20:00:13 +05:30
c72f3ece4e Merge pull request 'dev' (#24) from dev into main
Reviewed-on: #24
2024-12-20 12:26:22 +00:00
YasinShaikh123
85be7c891e update bugs 2024-12-20 17:17:18 +05:30
YasinShaikh123
e41d6b17b9 update env file ang bugs 2024-12-20 16:22:29 +05:30
3217605a0f Update README.md 2024-12-20 10:24:31 +00:00
b4d28387fa Update README.md 2024-12-20 09:36:28 +00:00
Swapnil Bendal
5411d4cd18 [fixed] - email message forget password 2024-12-12 17:47:57 +05:30
Swapnil Bendal
6a0aa17e2d [update]- profile page 2024-12-12 17:28:32 +05:30
Swapnil Bendal
471e4f32ab [fixed] - token server 2024-12-12 16:19:36 +05:30
YasinShaikh123
5d2c28f6ca update change password 2024-12-12 13:52:55 +05:30
YasinShaikh123
49f39e1c2c update subadmin 2024-12-12 13:39:19 +05:30
YasinShaikh123
f9c7bf9d5d update forget passsword 2024-12-12 13:20:12 +05:30
Swapnil Bendal
49beb9539a [fixed] - role 2024-12-11 20:30:52 +05:30
Swapnil Bendal
ed27ed6939 [update] - io cash, IO nav and pending 2024-12-11 19:36:06 +05:30
Swapnil Bendal
d84b3a0e35 [update] - role check 2024-12-11 18:47:43 +05:30
Swapnil Bendal
20c0c7840f [update] - constant 2024-12-11 15:45:56 +05:30
Swapnil Bendal
bbfd617b27 [update] - finalise 2024-12-11 14:54:13 +05:30
Swapnil Bendal
87e0716383 [added] - .env.exmple 2024-12-11 13:21:43 +05:30
Swapnil Bendal
c7d6a0fe36 [update] - token service 2024-12-11 12:59:19 +05:30
Swapnil Bendal
c83aaa411a Merge remote-tracking branch 'origin/Sprint-9' into audit-fix/swapnil 2024-12-11 11:42:24 +05:30
Swapnil Bendal
a2fe1435cb [update] - token service 2024-12-11 11:41:02 +05:30
6a9e0f9908 Merge pull request 'Yasin' (#23) from Yasin into Sprint-9
Reviewed-on: #23
2024-12-10 13:35:07 +00:00
YasinShaikh123
9c0b231e62 update changes 2024-12-09 20:10:47 +05:30
Swapnil Bendal
4ca28fd910 [untested] - code checkout in remote 2024-12-06 20:25:12 +05:30
YasinShaikh123
fed5125660 working create password 2024-12-06 20:04:20 +05:30
YasinShaikh123
8a297d02ef Merge branch 'Siddhesh' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into Yasin 2024-12-06 16:15:10 +05:30
YasinShaikh123
2c8965c16a working chenge password 2024-12-06 16:11:18 +05:30
9d4d5301e5 update 2024-12-06 16:09:36 +05:30
88dc9d14fe Merge pull request 'Yasin' (#22) from Yasin into Sprint-9
Reviewed-on: #22
2024-12-06 07:18:57 +00:00
YasinShaikh123
d567acfec8 update subadmin 2024-12-05 20:26:39 +05:30
YasinShaikh123
463325e603 update investor 2024-12-02 12:47:44 +05:30
84f869d9bd Merge branch 'release/sprint-8' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into Siddhesh 2024-12-02 12:26:26 +05:30
606ac68d85 Merge pull request 'update bug' (#20) from Yasin into release/sprint-8
Reviewed-on: #20
2024-12-02 06:55:15 +00:00
YasinShaikh123
537304f0fb update bug 2024-12-02 12:23:27 +05:30
YasinShaikh123
3b83b625c3 update Investment Details KYC 2024-11-25 20:24:39 +05:30
Swapnil Bendal
694dde0f14 [fixed] - view io details 2024-11-25 19:03:42 +05:30
Swapnil Bendal
4f8916036e [update] - condition on pending request for checkers 2024-11-25 16:45:52 +05:30
YasinShaikh123
0c21e99732 update comment changes 2024-11-25 16:41:19 +05:30
YasinShaikh123
5900f637be Merge branch 'release/sprint-8' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into Yasin 2024-11-25 15:52:33 +05:30
YasinShaikh123
2453b24d91 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into Yasin 2024-11-25 12:21:02 +05:30
Swapnil Bendal
3db9488444 [update] - counter in pending tab 2024-11-22 20:25:21 +05:30
bb30a71a60 Merge pull request 'modal api useEffect' (#19) from Yasin into release/sprint-8
Reviewed-on: #19
2024-11-22 14:27:49 +00:00
YasinShaikh123
526d2aecca modal api useEffect 2024-11-22 19:57:05 +05:30
e742f6c18d Merge pull request 'update modal view' (#18) from Yasin into release/sprint-8
Reviewed-on: #18
2024-11-22 14:24:42 +00:00
YasinShaikh123
d4c9a5521f update modal view 2024-11-22 19:54:12 +05:30
7a04ee0abb Merge pull request 'updaste' (#17) from Yasin into release/sprint-8
Reviewed-on: #17
2024-11-22 13:53:09 +00:00
YasinShaikh123
a12b2c9a2c updaste 2024-11-22 19:22:06 +05:30
0b2e48200a Merge pull request 'token change' (#16) from Yasin into release/sprint-8
Reviewed-on: #16
2024-11-22 13:35:11 +00:00
YasinShaikh123
bf7b3e1596 token change 2024-11-22 19:04:31 +05:30
059b711bc1 Merge branch 'release/sprint-8' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into release/sprint-8 2024-11-22 19:00:15 +05:30
b0c01f3a0c updaate role 2024-11-22 19:00:14 +05:30
272e94caf0 upodate 2024-11-22 18:57:49 +05:30
cd95aa35a7 Merge branch 'Yasin' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into Siddhesh 2024-11-22 18:53:22 +05:30
973ec656d0 Merge pull request 'update modal close' (#15) from Yasin into release/sprint-8
Reviewed-on: #15
2024-11-22 13:22:09 +00:00
YasinShaikh123
29a49150b7 update modal close 2024-11-22 18:51:05 +05:30
7a908a3284 Merge pull request 'updated' (#14) from Yasin into release/sprint-8
Reviewed-on: #14
2024-11-22 11:28:53 +00:00
YasinShaikh123
23c3997d06 updated 2024-11-22 16:56:31 +05:30
6779fdd672 Merge pull request 'Yasin' (#13) from Yasin into release/sprint-8
Reviewed-on: #13
2024-11-22 10:50:46 +00:00
YasinShaikh123
0ab898a3da update new bugs 2024-11-22 16:19:59 +05:30
YasinShaikh123
f3a8a80f4d update action 2024-11-22 13:32:28 +05:30
d1cbeee294 Merge pull request 'update bugs' (#12) from Yasin into release/sprint-8
Reviewed-on: #12
2024-11-22 07:22:50 +00:00
YasinShaikh123
1c4f975781 update bugs 2024-11-22 12:48:02 +05:30
17ffb864a3 Merge pull request 'Yasin' (#11) from Yasin into release/sprint-8
Reviewed-on: #11
2024-11-21 12:14:25 +00:00
YasinShaikh123
6ffcff58a2 update tabs 2024-11-21 17:42:27 +05:30
82612fa7f2 Merge pull request 'update color bugs' (#10) from main into release/sprint-8
Reviewed-on: #10
2024-11-21 10:36:20 +00:00
YasinShaikh123
470ba49c00 UPDATE invest modal 2024-11-21 16:05:51 +05:30
b29478a939 Merge pull request 'transaction panding' (#9) from Yasin into release/sprint-8
Reviewed-on: #9
2024-11-21 10:22:35 +00:00
YasinShaikh123
7ba524d2e4 transaction panding 2024-11-21 15:51:01 +05:30
cc58cdc9b7 Merge pull request 'update invest amount' (#8) from Yasin into release/sprint-8
Reviewed-on: #8
2024-11-21 10:17:31 +00:00
YasinShaikh123
9f54bfbc65 update invest amount 2024-11-21 15:46:10 +05:30
992cb13e1e Merge pull request 'update transaction status modal' (#7) from Yasin into release/sprint-8
Reviewed-on: #7
2024-11-19 13:53:15 +00:00
YasinShaikh123
85e3c34120 update transaction status modal 2024-11-19 19:19:45 +05:30
46672a34e2 Merge pull request 'update Io Cash Nav Transaction' (#6) from Yasin into release/sprint-8
Reviewed-on: #6
2024-11-18 13:36:45 +00:00
YasinShaikh123
61e49393fe update Io Cash Nav Transaction 2024-11-18 17:54:23 +05:30
YasinShaikh123
5b2efcd292 update color bugs 2024-11-14 16:28:55 +05:30
YasinShaikh123
5fc16b58ea working tabs👷‍♂️ 2024-11-14 16:04:20 +05:30
95c629533e update 2024-11-14 12:18:54 +05:30
YasinShaikh123
06548abf1e update tabs 2024-11-14 12:08:17 +05:30
YasinShaikh123
f2023cf7b3 update bugs 2024-11-11 12:48:06 +05:30
YasinShaikh123
77fc645767 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-11-07 19:32:57 +05:30
YasinShaikh123
9740fff33c update bugs 2024-11-07 19:32:55 +05:30
0f678fefc1 update 2024-11-06 11:49:34 +05:30
YasinShaikh123
986e531896 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-11-05 13:25:51 +05:30
YasinShaikh123
734dff43a1 but update 2024-11-05 13:25:39 +05:30
d244df302b update 2024-11-05 12:12:38 +05:30
YasinShaikh123
eb14139bcf update 30/10 2024-10-30 12:30:20 +05:30
YasinShaikh123
f5e7217304 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-24 19:11:30 +05:30
YasinShaikh123
30c51a5b34 Update 2024-10-24 19:05:52 +05:30
1f89ca0a1c IO date fixed 2024-10-23 20:07:55 +05:30
YasinShaikh123
82215f8569 user form working 2024-10-23 19:27:52 +05:30
YasinShaikh123
0199a46ed5 Revert "Update changes"
This reverts commit 8cfccf656d.
2024-10-22 20:12:34 +05:30
YasinShaikh123
8cfccf656d Update changes 2024-10-22 20:11:04 +05:30
YasinShaikh123
2e06b52881 Update investment Amount 2024-10-22 20:11:00 +05:30
f99e78a8df updated 2024-10-22 16:28:14 +05:30
YasinShaikh123
6f5e83a4bc investment update 2024-10-21 19:31:18 +05:30
YasinShaikh123
fff3689aeb Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-18 20:12:21 +05:30
YasinShaikh123
184f42ef59 package install 2024-10-18 20:12:14 +05:30
ccc75f50e5 final Commit 2024-10-18 20:01:12 +05:30
751aa6c673 updatee 2024-10-18 19:16:21 +05:30
d44b8aea0d Risky Update ⚠️ 2024-10-18 18:43:28 +05:30
5fd9fda73b Email custom notification update ✉️ 2024-10-18 16:29:26 +05:30
6216367d21 export button fix 2024-10-18 15:08:38 +05:30
a1b9f1c507 update filters 2024-10-18 14:57:21 +05:30
5ff6d5d07b commit 2024-10-18 13:49:30 +05:30
7f1106449f updated 2024-10-18 13:19:23 +05:30
da55f00f73 update Amount Invested 2024-10-18 12:20:10 +05:30
0c56759251 update siquence 2024-10-18 11:57:33 +05:30
cd1cf86fc9 update 2024-10-17 20:28:21 +05:30
9cd0a4e9c6 updaate 2024-10-17 19:58:27 +05:30
0b080733fb upddateed 2024-10-17 15:58:21 +05:30
YasinShaikh123
94b8be1130 updated 2024-10-17 13:39:39 +05:30
YasinShaikh123
3079e7f269 updateIOStatus Changes 2024-10-16 17:01:36 +05:30
YasinShaikh123
2147914c81 IO Nav Details Update 2024-10-15 13:11:05 +05:30
YasinShaikh123
3696ec5e59 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-11 20:16:39 +05:30
YasinShaikh123
7f5d3065ca update fix 2024-10-11 20:16:36 +05:30
28990bb9d6 risky commit ⚠️ 2024-10-11 17:36:22 +05:30
2f816f3c45 update roles nd coma 2024-10-11 17:32:36 +05:30
YasinShaikh123
61c81b100c Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-11 17:31:51 +05:30
YasinShaikh123
fd22732648 create amount update 2024-10-11 17:31:46 +05:30
2df6ea41a4 update roles ⚠️ 2024-10-11 17:26:27 +05:30
YasinShaikh123
739b755ec1 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-11 16:03:52 +05:30
6c16d142d6 UPDATE 2024-10-11 16:03:39 +05:30
YasinShaikh123
4a54d5c80e Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-11 16:03:30 +05:30
YasinShaikh123
47d75371bc create update date 2024-10-11 16:03:26 +05:30
YasinShaikh123
bc8f78d8d6 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-11 16:01:53 +05:30
915242c1f3 update 2024-10-11 15:49:08 +05:30
YasinShaikh123
30a9226f86 routes change 2024-10-11 15:21:41 +05:30
a6dd60140a upddate 2024-10-11 14:58:29 +05:30
512e2936ad update fawateer 2024-10-11 14:52:17 +05:30
c9edca64c8 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-11 14:51:56 +05:30
054323978c update 2024-10-11 13:35:50 +05:30
YasinShaikh123
a12ab874cf Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-11 13:33:59 +05:30
YasinShaikh123
14b24ffa00 fawateer history maker 2024-10-11 13:33:27 +05:30
e47db6c840 uopdate 2024-10-10 17:41:41 +05:30
YasinShaikh123
48e53ea176 update status 2024-10-10 17:38:46 +05:30
36cf9fa610 update 2024-10-10 17:09:17 +05:30
8a6d9102cb Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-10 17:08:56 +05:30
YasinShaikh123
88bea104f6 approve Request api 2024-10-10 16:16:28 +05:30
417164cb16 update 2024-10-10 16:12:46 +05:30
YasinShaikh123
c0ce32219e Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-10 15:47:58 +05:30
YasinShaikh123
aff088c7b7 FawateerApprove modal 2024-10-10 15:47:15 +05:30
a7d3703244 checker maker update 2024-10-10 15:39:23 +05:30
c41deb0534 updaate 2024-10-10 12:11:27 +05:30
92ebf64223 upate 2024-10-09 23:23:50 +05:30
36a68e2169 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-09 17:57:16 +05:30
46aa0c4631 update 2024-10-09 17:57:11 +05:30
YasinShaikh123
bd48e3fb06 date update 2024-10-07 17:22:51 +05:30
YasinShaikh123
90a433e312 deleteion date 2024-10-07 17:10:26 +05:30
8cd6a65143 notification sr fix 2024-10-05 20:59:41 +05:30
105f103fda IsAppNotificationEnabled value refactored 2024-10-05 20:41:26 +05:30
aa6c61e4a4 kyc & notification fix 2024-10-05 20:37:17 +05:30
b08030a412 notification fix 2024-10-05 20:14:59 +05:30
YasinShaikh123
149667436e updated Kyc 2024-10-04 18:06:38 +05:30
YasinShaikh123
bb724d88d1 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-10-04 11:55:47 +05:30
YasinShaikh123
1fabbd9b77 mobile changes 2024-10-04 11:55:40 +05:30
c51ae9081f 0.7 2024-10-03 13:48:09 +05:30
cfd811708a 0.6 2024-10-02 18:59:42 +05:30
6630723092 0.5 2024-10-02 18:55:32 +05:30
2ae81ef032 upated 2024-10-02 18:41:38 +05:30
10221c03d9 0.3 2024-10-02 14:21:26 +05:30
084641c561 0.2 2024-10-02 14:02:14 +05:30
7b24db3e00 WFH update 0.1 2024-10-02 14:01:31 +05:30
f8b33e3eb5 update 2024-10-02 13:32:35 +05:30
9fa42845c7 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel 🐞 2024-10-01 17:48:42 +05:30
YasinShaikh123
4bded669bf update 2024-10-01 17:45:35 +05:30
bc8d986f29 update 2024-10-01 17:44:15 +05:30
f5497ea6ac update 2024-10-01 17:44:06 +05:30
YasinShaikh123
7f1ef99b6c update amount👍 2024-09-24 18:43:51 +05:30
28978622f3 update 2024-09-23 15:21:42 +05:30
YasinShaikh123
9dca99b1c1 update active bg 2024-09-23 12:25:30 +05:30
YasinShaikh123
8546ca3247 update bank 2024-09-20 18:04:58 +05:30
YasinShaikh123
ae1cdc8811 update Bank Details👍 2024-09-20 18:04:22 +05:30
YasinShaikh123
1a1910c58c changes admin 2024-09-19 16:45:05 +05:30
YasinShaikh123
fdb9ccefa9 update bank country name👍 2024-09-17 15:39:39 +05:30
YasinShaikh123
a126d6515d admin pages Working👷‍♂️ 2024-09-17 15:33:27 +05:30
YasinShaikh123
ab175b4c76 table changes👨‍⚕️ 2024-09-13 19:43:38 +05:30
YasinShaikh123
c9c7a7be69 investor💲 2024-09-11 16:39:51 +05:30
YasinShaikh123
da70893c2b remove investort Currency Toggle Btn 2024-09-10 19:15:05 +05:30
YasinShaikh123
a53069f6f6 investor portfolio 2024-09-10 17:55:48 +05:30
YasinShaikh123
8b43ff77a9 withdrawal changes 2024-09-10 15:54:58 +05:30
YasinShaikh123
8cb693a4c4 investor changes 2024-09-10 12:54:09 +05:30
YasinShaikh123
7b91ad2720 Merge branch 'main' of https://github.com/WDI-Ideas/tanami_admin into dev 2024-09-10 12:00:08 +05:30
YasinShaikh123
416c5ce8aa Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-09-10 11:51:08 +05:30
a0a1849a79 Investment name update in portfolio 2024-09-09 21:46:07 +05:30
YasinShaikh123
a1d7b1e9f9 modal comma 2024-09-09 17:49:49 +05:30
3a327d63e0 Exit update 2024-09-03 18:03:20 +05:30
753f3d40c3 Risky Commit ☠️ 2024-09-03 13:45:57 +05:30
YasinShaikh123
226b8a39bc portfolio switch btn 2024-09-03 13:14:49 +05:30
fdbdc61cac Commit before realese 🎰 2024-09-03 13:10:31 +05:30
d1cc24a43b Table mod 🎨 2024-09-02 18:02:19 +05:30
c766b29c4b merge 📌 2024-09-02 17:43:54 +05:30
4ee94c1261 Merge branch 'dev' 📌 2024-09-02 17:43:28 +05:30
YasinShaikh123
c2d75c340c investor bankDetails 2024-09-02 17:41:00 +05:30
8cb25e9189 Merge branch 'dev' 🕊️ 2024-09-02 16:22:07 +05:30
d74481e7f4 [ Refetch update ] 🎭 2024-09-02 16:20:51 +05:30
YasinShaikh123
2281550345 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-09-02 16:12:20 +05:30
YasinShaikh123
6ca97a7ed3 failed gif 2024-09-02 16:12:13 +05:30
4b3313b1ea Table Update 🎰 2024-09-02 15:48:46 +05:30
YasinShaikh123
7a8ea42832 transection amount 2024-09-02 15:44:37 +05:30
1be4a00ced Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-09-02 15:35:12 +05:30
YasinShaikh123
c5d9b32e8d Revert "update"
This reverts commit 4957bd5032.
2024-09-02 15:31:57 +05:30
YasinShaikh123
4957bd5032 update 2024-09-02 15:28:40 +05:30
YasinShaikh123
f151b67e49 Io nav details date 2024-09-02 15:28:34 +05:30
b92de5e990 [Risky Update Two] 2024-08-30 18:41:14 +05:30
33bbd5f3e6 [Risky Update] 2024-08-30 18:16:38 +05:30
YasinShaikh123
0ab8ac73aa bank details 2024-08-30 18:15:58 +05:30
410f18f512 exit resoved 2024-08-30 18:03:11 +05:30
29fdb99366 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel 2024-08-30 17:49:01 +05:30
YasinShaikh123
867ad9e7b9 doller update 2024-08-30 17:47:10 +05:30
92fe9f66f9 final fixes 2024-08-30 17:42:27 +05:30
d2d18d435f align updated 2024-08-30 16:53:31 +05:30
ede34c1b41 color update 2024-08-30 16:50:05 +05:30
639a6cfae9 [Exit resolved] 2024-08-30 16:26:30 +05:30
68026ddd66 updated final fixes 2024-08-30 16:09:07 +05:30
YasinShaikh123
be451086b9 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-30 13:39:07 +05:30
YasinShaikh123
566d170226 update changes 2024-08-30 12:07:39 +05:30
60d3c7572d table updated 2024-08-30 12:07:10 +05:30
YasinShaikh123
dceec17c58 header ID 2024-08-29 17:02:43 +05:30
YasinShaikh123
a041cea256 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-28 19:35:43 +05:30
YasinShaikh123
91e362d6c2 mobile changes 2024-08-28 19:35:39 +05:30
b3c0662e77 update payment routes 2024-08-28 19:35:07 +05:30
YasinShaikh123
86ce31e86c Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-26 20:28:41 +05:30
YasinShaikh123
0df81c1169 date formate 2024-08-26 20:28:39 +05:30
31eb1605bf Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-26 19:54:15 +05:30
8dc5eecad9 upmerge 2024-08-26 19:54:10 +05:30
YasinShaikh123
508d7fe5b5 changes date 2024-08-26 19:53:59 +05:30
YasinShaikh123
9208535613 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-26 18:59:39 +05:30
YasinShaikh123
9916d6618e amount changes 2024-08-26 18:59:29 +05:30
2f7f907a89 Updated Sr No 2024-08-26 18:58:58 +05:30
8b0454b16a updated 2024-08-26 16:20:54 +05:30
YasinShaikh123
f7b06bfd19 pagination 2024-08-26 16:05:03 +05:30
YasinShaikh123
64b881dd89 bugs 2024-08-26 14:12:56 +05:30
YasinShaikh123
fba3ad2212 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-26 12:40:59 +05:30
YasinShaikh123
026a563d84 error message 2024-08-26 12:32:45 +05:30
b713845df6 update 2024-08-26 12:11:38 +05:30
79bcf851d9 [Coma update after sprint 5] 2024-08-23 22:18:50 +05:30
3a7d174766 coma updated after sprint 5 2024-08-23 22:01:17 +05:30
YasinShaikh123
6d315ba2ef comma changes 2024-08-23 20:46:55 +05:30
YasinShaikh123
7748872e29 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-23 19:44:35 +05:30
YasinShaikh123
b5f7250691 comma 2024-08-23 19:44:26 +05:30
0db739c557 update 2024-08-23 19:44:03 +05:30
3165c11af7 update 2024-08-23 18:09:31 +05:30
c97de1314e updated 2024-08-23 18:06:32 +05:30
287fb80f9e Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel 2024-08-23 18:03:29 +05:30
b690a55ed3 updated 2024-08-23 18:03:18 +05:30
YasinShaikh123
b8390bfad9 profile update 2024-08-23 18:02:50 +05:30
YasinShaikh123
5e4ebe3fb5 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-23 18:00:53 +05:30
YasinShaikh123
8b97add9e0 portpolio api 2024-08-23 18:00:44 +05:30
25226d5464 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-23 16:22:43 +05:30
3131ff8610 update 2024-08-23 16:22:40 +05:30
YasinShaikh123
187841b178 withdraw request 2024-08-23 16:22:04 +05:30
YasinShaikh123
849b89efc8 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-23 13:06:23 +05:30
116db3b922 updated 2024-08-23 13:05:59 +05:30
YasinShaikh123
4313057012 conflict mrg 2024-08-23 12:49:43 +05:30
YasinShaikh123
cdd0d670ba Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-23 12:48:50 +05:30
7c3a6112d2 update delewtion 2024-08-23 12:24:34 +05:30
e3ccc3efbc Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel 2024-08-23 12:24:19 +05:30
YasinShaikh123
129a79c669 drawar history 2024-08-23 12:23:44 +05:30
b7ce3de128 update 2024-08-22 18:48:45 +05:30
YasinShaikh123
97cfd45545 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-22 18:47:49 +05:30
YasinShaikh123
e0664d8e21 drawal request ui 2024-08-22 18:47:45 +05:30
d009848068 update 2024-08-22 14:43:57 +05:30
YasinShaikh123
afb538cc22 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-22 12:10:44 +05:30
YasinShaikh123
e0b48abfd5 drawal request ui 2024-08-22 12:10:34 +05:30
9083d94813 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-22 12:10:11 +05:30
323d09f88d [ Displlay order updated ] 2024-08-22 12:10:07 +05:30
46d8031d4d deposite api 2024-08-21 16:04:05 +05:30
fb39e32b95 update endPoints 2024-08-21 13:03:50 +05:30
670e4d36a9 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into dev 2024-08-20 20:24:40 +05:30
YasinShaikh123
26777e2ada investor-amount 2024-08-20 20:22:37 +05:30
51f3b512a7 update 2024-08-20 20:22:19 +05:30
YasinShaikh123
d7012f2692 selected css 2024-08-20 19:10:58 +05:30
YasinShaikh123
e07a92ba03 mobile downloader 2024-08-20 15:55:06 +05:30
YasinShaikh123
adb4bd5d27 Merge branch 'dev' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into feature-dev 2024-08-20 14:53:05 +05:30
cfda3264fc [.env update] 2024-08-20 14:52:13 +05:30
YasinShaikh123
1569afe4f0 mobile height 2024-08-20 13:47:46 +05:30
YasinShaikh123
4a3072b4d3 io mobile 2024-08-20 13:00:22 +05:30
4516c70406 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel 2024-08-20 12:19:06 +05:30
50f87869be Edit update 2024-08-20 12:19:02 +05:30
d69e4a203f Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel 2024-08-20 11:26:40 +05:30
6e4c794d2b update 2024-08-17 20:38:06 +05:30
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
208 changed files with 30282 additions and 5765 deletions

28
.env.example Normal file
View File

@@ -0,0 +1,28 @@
# Default Value Maker
VITE_MAKER="Maker"
# Default Value Checker
VITE_CHECKER="Checker"
# Role Encryption key
VITE_ROLE_ENCRYPTION_KEY="export"
# Super Admin
VITE_SUPER_ADMIN_ID=1
# BaseURL
VITE_BAS_URL="your_base_url"
# BaseURL for Images
VITE_IMAGE_URL="your_base_url"
# Max try re-genrate token
VITE_MAX_TRY_REGENRATE_TOKEN=3
VITE_STATUS_DRAFT="Draft"
VITE_STATUS_PROCESSING="Processing"
VITE_STATUS_OPEN="Open"
VITE_STATUS_CLOSED="Closed"
VITE_STATUS_EXITED="Exited"
VITE_STATUS_CANCELLED="Cancelled"
VITE_STATUS_DEACTIVATE="DeActivate"

117
README.md
View File

@@ -1,10 +1,113 @@
# React + Vite
# **Tanami Capital**
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
**Tanami** is a cutting-edge fintech platform designed to streamline investment opportunities for users in the Gulf region. It features two main components:
Currently, two official plugins are available:
- **Admin Panel:** A web-based dashboard for managing users, monitoring transactions, and overseeing investments efficiently.
- **Mobile Application:** A user-friendly app that empowers individuals to invest in diverse asset classes, including real estate, private equity, and other financial instruments located in Gulf countries.
---
## **Key Features**
- **Regional Focus:** Exclusively operational in Gulf countries, offering investment opportunities tailored to the region.
- **Diverse Investment Options:** Enables users to invest in financial instruments like real estate and private equity with ease and transparency.
- **Comprehensive Admin Tools:** The admin panel offers robust tools for tracking and managing platform activity.
---
## **Current Status**
The project is **live and operational**, catering specifically to the investment needs of users in the Gulf region.
---
## **Table of Contents**
- [Installation](#installation)
- [Usage](#usage)
- [Environment Variables](#environment-variables)
- [Scripts](#scripts)
- [License](#license)
---
## **Installation**
### **Prerequisites**
- [Node.js](https://nodejs.org/) (version 14 or higher recommended)
- [npm](https://www.npmjs.com/) (bundled with Node.js)
### **Steps**
1. Clone the repository:
```bash
git clone http://git.wdipl.com/Siddhesh.More/tanami-admin-panel.git
```
2. Navigate to the project directory:
```bash
cd tanami-admin-panel
```
3. Install dependencies:
```bash
npm install
```
---
## **Usage**
### **Development Mode**
1. Start the development server:
```bash
npm run dev
```
2. Open your browser and navigate to:
```
http://localhost:5173/
```
### **Production Mode**
1. Install [PM2](https://pm2.keymetrics.io/) globally for process management:
```bash
npm install pm2 -g
```
2. Build the application (if applicable):
```bash
npm run build
```
3. Serve the application using PM2:
```bash
pm2 serve ./dist <port_number> --spa --name=<application_name>
```
Replace:
- `./dist` with your build directory.
- `<port_number>` with the desired port (e.g., `3000`).
- `<application_name>` with the name of your application.
4. Save the PM2 process list and enable startup on system reboot:
```bash
pm2 save
pm2 startup
```
---
## **Environment Variables**
Create a `.env` file in the root directory based on the structure of [`.env.example`](.env.example).
---
## **Scripts**
| Script | Description |
|---------------------|-------------------------------------------------------------|
| `npm run dev` | Starts the app in development mode with `Vite` server. |
| `npm run build` | Builds the app for production. |
| `npm run lint` | Runs ESLint to check for code quality issues. |
| `npm run preview` | Previews the production build locally. |
---
## **License**
This project is licensed under the [MIT License](LICENSE).
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
# rubix-admin-panel
# tanami-admin-panel

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>

2357
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,7 @@
"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",
@@ -30,7 +31,8 @@
"react-dom": "^18.2.0",
"react-hook-form": "^7.51.3",
"react-icons": "^5.1.0",
"react-quill": "^0.0.2",
"react-phone-input-2": "^2.15.1",
"react-quill": "^2.0.0",
"react-redux": "^9.1.1",
"react-router-dom": "^6.22.3",
"redux-persist": "^6.0.0",

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;
}

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);
};
@@ -52,12 +57,15 @@ const App = () => {
<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" ? (
localStorage.getItem('accessToken') && localStorage.getItem('refreshToken') ? (
isAuthenticate || isAuthenticatedInCookie === "true" ? (
// localStorage.getItem('accessToken') && localStorage.getItem('refreshToken') ? (
// true ? (
<DefaultLayout isOnline={isOnline} />
) : (
<Login />

View File

@@ -31,6 +31,7 @@ import Header from "../Header";
import ToastBox from "../ToastBox";
import BannerMainCard from "./BannerMainCard";
const AddBanner = ({ createApi, navigateLink, title, center }) => {
const toast = useToast();
const navigate = useNavigate();

View File

@@ -1,37 +1,32 @@
import React, { forwardRef } from 'react';
import { Input } from "@chakra-ui/react";
// export const formatCurrency = (value) => {
// if (!value) return '';
// const [integer, decimal] = value.split('.');
// const formattedInteger = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
// return decimal ? `${formattedInteger}.${decimal}` : formattedInteger;
// };
export const formatCurrency = (value) => {
if (!value) return '';
const [integer, decimal] = value?.split('.');
const formattedInteger = integer?.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return decimal ? `${formattedInteger}.${decimal}` : formattedInteger;
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, '');
value = value.replace(/[^0-9.]/g, '');
// Ensure only one decimal point
const parts = value?.split('.');
const parts = value.split('.');
if (parts.length > 2) {
value = parts[0] + '.' + parts?.slice(1)?.join('');
value = parts[0] + '.' + parts.slice(1).join('');
}
onChange(value); // Pass the raw value to parent or use it directly
// 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 (
@@ -39,6 +34,7 @@ const CurrencyInput = forwardRef(({ value, onChange, ...props }, ref) => {
{...props}
ref={ref} // Forward ref here
type="text"
focusBorderColor="forestGreen.300"
value={formatCurrency(value)}
onChange={handleChange}
/>
@@ -46,3 +42,50 @@ const CurrencyInput = forwardRef(({ value, onChange, ...props }, ref) => {
});
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

@@ -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,79 +9,170 @@ 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
console.log(data);
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
color={"purple.900"}
textAlign={
tableHeadRow.length - 1 === index || centered
? "center"
: "left"
}
key={index}
p={3}
width="20px" // Adjust width as needed
color={"#004118"}
whiteSpace="normal" // Allow text to wrap
wordBreak="normal" // Ensure long words break properly
overflowWrap="normal" // Break long words if necessary
textTransform={'none'}
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: TABLE_PAGINATION?.size }).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={
@@ -91,6 +182,7 @@ const DataTable = ({
}
color={"gray.600"}
key={i}
fontWeight={500}
style={{
whiteSpace: "nowrap",
textOverflow: "ellipsis",
@@ -109,4 +201,4 @@ const DataTable = ({
);
};
export default DataTable;
export default NormalTable;

View File

@@ -22,12 +22,14 @@ 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];
@@ -69,12 +71,13 @@ const FormField = ({
align,
maxLength,
dateValue,
closingDate,
...props
}) => (
<FormControl
w={width ? width : "49%"}
isInvalid={errors[name]}
isRequired={type === "date" ? false: isRequired}
isRequired={type === "date" ? true: isRequired}
mb={2}
>
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
@@ -84,6 +87,7 @@ const FormField = ({
control={control}
name={name}
defaultValue={value}
rules={rules}
render={({ field }) => {
if (type === "select") {
@@ -404,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"
@@ -465,7 +468,7 @@ const FormField = ({
} else if(type === 'date'){
return (
<Input
position={'relative'}
position={'relative'}
bg={"#F5F8F6"}
focusBorderColor="forestGreen.300"
size={"sm"}
@@ -477,12 +480,12 @@ const FormField = ({
placeholder={placeHolder ? placeHolder : label}
textAlign={arabic ? "right" : align ? align : "left"}
_placeholder={{ fontSize: "sm" }}
min={type === "date" ? today : undefined}
maxLength={maxLength}
// 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}
/>
);
@@ -506,7 +509,21 @@ const FormField = ({
// value={dateValue}
/>
);} else{
);}
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 Shariah Compliant</Text>
</Checkbox>
</HStack>
);} else{
return (
<Input
bg={"#F5F8F6"}

View File

@@ -60,7 +60,8 @@ const FormInputMain = ({
handleInputChange,
align,
maxLength,
dateValue
dateValue,
closingDate
},
key
) => (
@@ -89,6 +90,7 @@ const FormInputMain = ({
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";
@@ -34,8 +35,13 @@ const FormInputView = ({
<Heading as="h6" size="xs" mt={index === 0 ? 3 : 4}>
{section}
</Heading>
{/* <Box display={"flex"} gap={0}> */}
<Box key={index} 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" ? (
@@ -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,7 +123,30 @@ 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>
<Box key={id} w={!width ? "49%" : width}>
<FormLabel key={id} color={"gray.500"} fontSize={"xs"}>
{label}
@@ -141,7 +167,6 @@ const FormInputView = ({
)
)}
</Box>
{/* </Box> */}
{index <
Object.entries(groupedFields, groupedFieldsTwo).length - 1 && (
<Divider />

View File

@@ -13,16 +13,18 @@ import {
Portal,
Text,
useColorMode,
useDisclosure,
} from "@chakra-ui/react";
import React, { useContext } from "react";
import React, { useContext, useRef } from "react";
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";
import ChangePassword from "../Pages/ChangePassword";
const HeaderMain = ({
link,
@@ -31,10 +33,12 @@ const HeaderMain = ({
icon,
logOutHandler,
slideDirecttion,
data,
}) => {
const navigate = useNavigate()
const navigate = useNavigate();
const { colorMode, toggleColorMode } = useContext(GlobalStateContext);
const { isOpen, onOpen, onClose } = useDisclosure();
const firstField = useRef();
return (
<Box
@@ -46,32 +50,31 @@ const HeaderMain = ({
zIndex={999}
>
<HStack>
{/* <ArrowBackIcon onClick={()=>navigate(-1)} /> */}
<Text
as={"span"}
fontWeight={"500"}
// color={"forestGreen.500"}
className="fs-6 "
>
{/* <icon /> */}
{title}
</Text>
{/* <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"}>
<Box onClick={onOpen}>
<PopoverBody className="web-text-medium pointer ">
Help & Support
Change Password
</PopoverBody>
</Link>
</Box>
<PopoverFooter
onClick={logOutHandler}
className="web-text-medium pointer link"
@@ -90,7 +93,7 @@ const HeaderMain = ({
boxSize={37}
name="Tanami M"
src={logoMini}
bg={'green.100'}
bg={"green.100"}
// p={1}
/>
<Box
@@ -100,10 +103,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>
@@ -113,6 +116,11 @@ const HeaderMain = ({
{/* <Box onClick={() => toggleColorMode()} as="span" p={2} rounded={'lg'} className="link pointer">
{colorMode === "light"? <MdOutlineDarkMode /> :<MdOutlineLightMode />}
</Box> */}
<ChangePassword
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
</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

@@ -1,18 +1,19 @@
import { Box, Spinner, Text } from "@chakra-ui/react";
import React from "react";
import './FullscreenLoaders.css'
import "./FullscreenLoaders.css";
const FullscreenLoaders = ({height}) => {
const FullscreenLoaders = ({ height }) => {
return (
<Box
display={"flex"}
justifyContent={"center"}
flexDirection={'column'}
flexDirection={"column"}
alignItems={"center"}
w={"100%"}
h={height ? height: "100vh"}
h={height ? height : "100vh"}
gap={4}
><div className="dot-spinner">
>
{/* <div className="dot-spinner">
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
@@ -21,8 +22,17 @@ const FullscreenLoaders = ({height}) => {
<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> */}
</div> */}
{/* <Text color='#004717' fontSize={'md'} fontWeight={500}>Loading...</Text> */}
{/* <div className="spinner-grow" style={{backgroundColor:"#004118"}} role="status" /> */}
<Spinner
thickness="4px"
speed="0.65s"
emptyColor="#fff"
color="#004118"
size="lg"
/>
</Box>
);
};

View File

@@ -1,19 +1,31 @@
import React from "react";
import './FullscreenLoaders.css'
import { Spinner } from "@chakra-ui/react";
const Loader01 = () => {
return (
// <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>
<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>
// <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>
<Spinner color='green.900' />
);
};

View File

@@ -10,6 +10,7 @@ import {
ModalHeader,
ModalOverlay,
Progress,
Spinner,
Stack,
Text,
} from "@chakra-ui/react";
@@ -25,20 +26,26 @@ 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 }) => {
const MobileView = ({ isOpen, onClose, finalRef, actionId }) => {
const [time, setTime] = useState(new Date());
const navigate = useNavigate();
const params = useParams();
const id = params?.id;
const id = actionId;
const {
data,
data: IObyID,
isLoading: IObyIDisLoading,
error: IObyIDerror,
} = useGetIOByIdQuery(id, { skip: !id });
console.log(data);
console.log(IObyID);
const keyMerits = IObyID?.data?.keyMerits || [];
const artifactsImage = IObyID?.data?.artifactsImage || [];
useEffect(() => {
const timer = setInterval(() => {
@@ -52,10 +59,17 @@ const MobileView = ({ isOpen, onClose, finalRef }) => {
return date.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
hour12: false,
hour12: true,
});
};
console.log(
calculatePercentage(
IObyID?.data?.totalAmtInvestmentInUSD,
IObyID?.data?.goalAmount
)
);
return (
<Modal
display={"flex"}
@@ -79,7 +93,17 @@ const MobileView = ({ isOpen, onClose, finalRef }) => {
display={"flex"}
justifyContent={"center"}
h={"600px"}
w={"330px"}
w={"320px"}
sx={{
"@media (max-width: 2024px)": {
height: "695px",
width: "360px",
},
"@media (max-width: 1440px)": {
height: "600px",
width: "320px",
},
}}
>
<Image
h={"100%"}
@@ -98,278 +122,351 @@ const MobileView = ({ isOpen, onClose, finalRef }) => {
pt={"36px"}
px={"15px"}
>
<Box>
{IObyIDisLoading ? (
<Box
display={"flex"}
justifyContent={"center"}
alignItems={"center"}
position={"absolute"}
left={"30px"}
top={"18px"}
h={"100%"}
>
<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> */}
<Spinner thickness="3px" color="purple.900" size="lg" />
</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"}
>
<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"}
) : (
<>
<Box>
<Box
display={"flex"}
alignItems={"center"}
fontWeight={500}
backgroundColor={"#fff"}
p={"7px 12px"}
borderRadius={"20px"}
position={"absolute"}
left={"30px"}
top={"18px"}
>
<LuClock color="#d8804e" />{" "}
<Text mb={0} ml={1}>
Closing Date Aug 23 2024
<Text ml={1} mb={0}>
<GiNetworkBars fontSize={"10px"} />
</Text>
</Text>
<Image
borderTopLeftRadius={"20px"}
borderTopRightRadius={"20px"}
h={"130px"}
w={"100%"}
src={mobileBanner}
/>
</Box>
<Stack mt="3" bg={"#fff"} py={4} px={4}>
<Text
fontSize={"sm"}
fontWeight={"500"}
color={"#000"}
mb={0}
>
Guinevere Gates
</Text>
<Heading fontSize="16px" color={"#004717"}>
BHD 46,258
</Heading>
<Progress
colorScheme="green"
size="sm"
value={20}
borderRadius={"3px"}
/>
<Text
color={"#4e4e4e"}
fontSize={"xs"}
fontWeight={600}
mb={0}
>
0.0% funded
</Text>
<Text
fontSize={"xs"}
fontWeight={500}
mb={0}
color={"#9d9d9d"}
>
fugit eligendi dolore dolore et
</Text>
</Stack>
<Box py={4} px={4}>
<Box display={"flex"} justifyContent={"space-between"}>
<Text
fontSize={"10px"}
mb={1}
fontWeight={600}
color={"#616161"}
>
Sponsor name:
<Text ml={1} mb={0} fontSize={"10px"}>
{formatTime(time)}
</Text>
<Text fontSize={"10px"} mb={1} fontWeight={600}>
Scott Simon
</Text>
</Box>
<Box display={"flex"} justifyContent={"space-between"}>
<Text
fontSize={"10px"}
mb={1}
fontWeight={600}
color={"#616161"}
>
Estimated return:
</Text>
<Text fontSize={"10px"} mb={1} fontWeight={600}>
A provident veniam
</Text>
</Box>
<Box display={"flex"} justifyContent={"space-between"}>
<Text
fontSize={"10px"}
mb={1}
fontWeight={600}
color={"#616161"}
>
Hoiding period:
</Text>
<Text fontSize={"10px"} mb={1} fontWeight={600}>
Eius eiusmod exericit
</Text>
</Box>
<Box display={"flex"} justifyContent={"space-between"}>
<Text
fontSize={"10px"}
mb={1}
fontWeight={600}
color={"#616161"}
>
Minimum investment:
</Text>
<Text fontSize={"10px"} mb={1} fontWeight={600}>
BHD 1
</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 display={"flex"} alignItems={"center"}>
<Image
width={"30px"}
me={2}
src="https://tanami.betadelivery.com/public/icons/icon3.svg"
/>
<Text fontSize={"xs"} mb={0}>
Sit sunt consequunt Dolores minim suscip
</Text>
</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"
/>
<Text fontSize={"xs"} mb={0}>
Merrill Rocha
<Text ml={"5px"} mb={0}>
<GrLinkedinOption fontSize={"10px"} />
</Text>
{/* <Text ml={1} mb={0}><FiInstagram fontSize={"10px"} /></Text> */}
</Box>
<Box
display={"flex"}
alignItems={"center"}
justifyContent={"space-between"}
position={"absolute"}
right={"36px"}
top={"17px"}
>
<Text mb={0} fontSize={"sm"}>
0.03 mb
<Text mb={0}>
<BiWifi fontSize={"14px"} />
</Text>
<Text ml={1} mb={0}>
<IoBatteryHalf fontSize={"15px"} />
</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 style={{ borderRadius: "18px" }}>
<source
src="https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4"
type="video/mp4"
style={{ height: "200px" }}
/>
</video>
</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
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>

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 = 1,
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, 500]?.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

@@ -0,0 +1,202 @@
import {
Box,
Button,
Checkbox,
FormControl,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useBoolean,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ReactQuill from "react-quill";
export const conformModalSchema = yup.object().shape({
comment: yup
.string()
.min(2, "Minimum length should be 2 characters.")
.max(150, "Maximum length should be 150 characters.")
// .matches(/^[^\d]+$/, "Sponsor Name cannot contain numbers")
.required("Comment is required"),
subject: yup.string().notRequired(),
emailTemplate: yup.string().notRequired(),
});
const ConfirmReversalPopups = ({
isOpen,
onClose,
handleConfirm,
isLoading,
}) => {
const {
watch,
register,
reset,
handleSubmit,
setValue,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
mode: "all",
});
const [richTextValue, setRichTextValue] = useState("");
useEffect(() => {
setValue("emailTemplate", richTextValue);
}, [richTextValue]);
// Reset the form when the modal closes
useEffect(() => {
if (!isOpen) {
reset(); // Clear the form state
}
}, [isOpen, reset]);
const [emailApproval, setEmailApproval] = useBoolean(false);
const modules = {
toolbar: [
// [{ header: "1" }, { header: "2" },
// // { font: [] }
// ],
// [{ size: [] }],
["bold", "italic", "underline", "strike", "blockquote"],
[{ list: "ordered" }, { list: "bullet" }],
["clean"],
],
};
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Approve</ModalHeader>
<ModalCloseButton />
<Box
as="form"
onSubmit={handleSubmit((data) => {
handleConfirm(data);
reset();
onClose();
})}
>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comment"
{...register("comment")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comment...."}
rounded={"md"}
resize={"none"}
mb={2}
/>
{errors.comment ? (
<Text fontSize="xs" color="red">
{errors.comment.message}
</Text>
) : (
<Text fontSize="xs" color="gray.500">
Maximum length should be 150 characters. You have entered{" "}
{watch()?.comment?.length || 0} characters.
</Text>
)}
</FormControl>
<Checkbox
colorScheme="forestGreen"
onChange={setEmailApproval.toggle}
defaultChecked={emailApproval}
>
<Text mb={0} fontSize={"sm"}>
Send an email to the user upon approval
</Text>
</Checkbox>
{emailApproval && (
<Box className="messageBox">
<FormControl mb={4}>
<FormLabel fontSize="sm" mb={1}>
Subject
</FormLabel>
<Input
focusBorderColor="green.400"
name="fileName"
{...register("subject")}
fontSize="sm"
type="text"
size="sm"
/>
</FormControl>
<FormControl mb={12}>
<FormLabel fontSize="sm" mb={1}>
Message
</FormLabel>
<ReactQuill
theme="snow"
style={{
height: 150,
}}
value={richTextValue}
onChange={setRichTextValue}
modules={modules}
placeholder="Start typing here..."
/>
</FormControl>
</Box>
)}
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
type="submit"
fontWeight={400}
>
Send
</Button>
</ModalFooter>
</Box>
</ModalContent>
</Modal>
);
};
ConfirmReversalPopups.propTypes = {
isOpen: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
handelApproved: PropTypes.func.isRequired,
isLoading: PropTypes.func.isRequired,
richTextValue: PropTypes.any.isRequired,
setRichTextValue: PropTypes.any.isRequired,
};
export default ConfirmReversalPopups;

View File

@@ -0,0 +1,133 @@
import {
Box,
Button,
FormControl,
FormLabel,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
} from "@chakra-ui/react";
import React, { useEffect } from "react";
import PropTypes from "prop-types";
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()
.min(2, "Minimum length should be 2 characters.")
.max(150, "Maximum length should be 150 characters.")
// .matches(/^[^\d]+$/, "Sponsor Name cannot contain numbers")
.required("Comment is required"),
});
const InitiateReversalPopup = ({
isOpen,
onClose,
handelApproved,
isLoading,
}) => {
const {
watch,
register,
reset,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
mode: "all",
});
// Reset the form when the modal closes
useEffect(() => {
if (!isOpen) {
reset(); // Clear the form state
}
}, [isOpen, reset]);
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Reversal Reason</ModalHeader>
<ModalCloseButton />
<Box
as="form"
onSubmit={handleSubmit((data) => {
handelApproved(data);
reset();
onClose();
})}
>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="fileName"
{...register("comment")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comment...."}
rounded={"md"}
resize={"none"}
mb={2}
/>
{errors.comment ? (
<Text fontSize="xs" color="red">
{errors.comment.message}
</Text>
) : (
<Text fontSize="xs" color="gray.500">
Maximum length should be 150 characters. You have entered{" "}
{watch()?.comment?.length || 0} characters.
</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"}
type={"submit"}
isDisabled={isLoading}
>
Send
</Button>
</ModalFooter>
</Box>
</ModalContent>
</Modal>
);
};
InitiateReversalPopup.propTypes = {
isOpen: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
handelApproved: PropTypes.func.isRequired,
};
export default InitiateReversalPopup;

View File

@@ -0,0 +1,132 @@
import {
Box,
Button,
FormControl,
FormLabel,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
} from "@chakra-ui/react";
import React, { useEffect } from "react";
import PropTypes from "prop-types";
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()
.min(2, "Minimum length should be 2 characters.")
.max(150, "Maximum length should be 150 characters.")
// .matches(/^[^\d]+$/, "Sponsor Name cannot contain numbers")
.required("Comment is required"),
});
const RejectReversalPopups = ({
isOpen,
onClose,
handelApproved,
isLoading,
}) => {
const {
watch,
register,
reset,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
mode: "all",
});
// Reset the form when the modal closes
useEffect(() => {
if (!isOpen) {
reset(); // Clear the form state
}
}, [isOpen, reset]);
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Reject</ModalHeader>
<ModalCloseButton />
<Box
as="form"
onSubmit={handleSubmit((data) => {
handelApproved(data);
reset();
onClose();
})}
>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={6}
focusBorderColor="green.400"
name="comment"
{...register("comment")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comment...."}
rounded={"md"}
resize={"none"}
mb={2}
/>
{errors.comment ? (
<Text fontSize="xs" color="red">
{errors.comment.message}
</Text>
) : (
<Text fontSize="xs" color="gray.500">
Maximum length should be 150 characters. You have entered{" "}
{watch()?.comment?.length || 0} characters.
</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"}
type="submit"
fontWeight={400}
>
Send
</Button>
</ModalFooter>
</Box>
</ModalContent>
</Modal>
);
};
RejectReversalPopups.propTypes = {
isOpen: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
handelApproved: PropTypes.func.isRequired,
isLoading: PropTypes.func.isRequired,
};
export default RejectReversalPopups;

View File

@@ -0,0 +1,65 @@
import { Box, Text } from "@chakra-ui/react";
import React from "react";
const RoleSwitchButton = ({ isSwitchOn, setIsSwitchOn }) => {
// const [isSwitchOn, setIsSwitchOn] = useState(false);
// const audio = useRef();
const switchOnChangeHandle = () => {
setIsSwitchOn(!isSwitchOn);
// if (audio.current) {
// audio.current.play();
// }
};
return (
<Box display="flex" alignItems="center">
<Box
as="button"
display="flex"
justifyContent="normal"
alignItems="center"
// justifyContent={isSwitchOn ? "flex-end" : "flex-start"}
width="85px"
height="24px"
borderRadius="20px"
backgroundColor={isSwitchOn ? "#00ffcc" : "#b3ff99"}
onClick={switchOnChangeHandle}
position="relative"
fontSize="12px"
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(61px)" : "translateX(0)",
transition: "transform 0.2s",
left:'2px',
top:'2px'
}}
>
<Text
fontWeight="500"
zIndex={1}
position="absolute"
mb={0}
color={isSwitchOn ? "#000" : "#000"}
left={isSwitchOn ? "10px" : "auto"}
right={isSwitchOn ? "auto" : "10px"}
>
{isSwitchOn ? "Maker" : "Checker"}
</Text>
</Box>
{/* <audio ref={audio} src={audioClick} /> */}
</Box>
);
};
export default RoleSwitchButton;

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>
);

View File

@@ -1,14 +1,17 @@
import dns from "node:dns"
import * as XLSX from 'xlsx';
import CryptoJS from "crypto-js";
export const generateSerialNumber = (index, currentPage = 1, pageSize = 1) => {
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];
}
@@ -30,7 +33,7 @@ export function removeTrailingZeros(value) {
}
export function getCountdownTimer(utcDateString) {
export function getCountdownTimer(utcDateString) {
// Parse the UTC datetime string into a Date object
const targetDate = new Date(utcDateString);
const now = new Date();
@@ -53,10 +56,14 @@ export function removeTrailingZeros(value) {
const remainingMinutes = minutes % 60;
const remainingSeconds = seconds % 60;
return `${remainingDays === 0 ? "": remainingDays+"d"} ${remainingHours === 0 ? "": remainingHours+"h"} ${remainingMinutes}m `;
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
@@ -87,30 +94,33 @@ export function debounce(func, delay) {
async function resolveMx(domain, recordType) {
async function resolveMx(domain) {
return new Promise((resolve, reject) => {
dns.resolveMx(domain, (err, mxRecords) => {
if (err) {
reject(err);
return;
}
const addresses = mxRecords.map((mxRecord) => mxRecord.exchange);
resolve(addresses);
});
dns.resolveMx(domain, (err, mxRecords) => {
if (err) {
reject(err);
return;
}
const addresses = mxRecords.map((mxRecord) => mxRecord.exchange);
resolve(addresses);
});
});
}
// Async function to check email address validity
export async function checkEmailValidity(email) {
try {
const domain = email?.split("@")[1];
const addresses = await resolveMx(domain, "MX");
const domain = email?.split('@')[1];
const addresses = await resolveMx(domain, 'MX');
console.log(addresses);
if (addresses && addresses?.length > 0) {
return true;
}
return false; // No MX record exists
if (addresses && addresses?.length > 0) {
return true;
}
return false; // No MX record exists
} catch (err) {
return false; // Error occurred
console.log(err);
return false; // Error occurred
}
}
@@ -118,15 +128,118 @@ export async function checkEmailValidity(email) {
// 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'
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}`;
}
// Encrypt a string
export const encryptString = (text) => {
const ciphertext = CryptoJS.AES.encrypt(text, import.meta.env.VITE_ROLE_ENCRYPTION_KEY).toString();
return ciphertext;
};
// Decrypt a string
export const decryptString = (ciphertext) => {
const bytes = CryptoJS.AES.decrypt(ciphertext, import.meta.env.VITE_ROLE_ENCRYPTION_KEY);
const originalText = bytes.toString(CryptoJS.enc.Utf8);
return originalText;
};
export const SUPER_ADMIN_ID = Number(import.meta.env.VITE_SUPER_ADMIN_ID) || 1
export const MAKER_ID = import.meta.env.VITE_MAKER_ID || 1
export const CHECKER_ID = import.meta.env.VITE_CHECKER_ID || 2
export const isMaker = (role = decryptString(localStorage?.getItem("role"))) => role === import.meta.env.VITE_MAKER;
export const isChecker = (role = decryptString(localStorage?.getItem("role"))) => role === import.meta.env.VITE_CHECKER;

View File

@@ -1,2 +1,3 @@
export const TABLE_PAGINATION = { page: 1, size:20 }
export const IMAGE_URI = import.meta.env.VITE_API_IMAGE_URL
export const TABLE_PAGINATION = { page: 1, size: 20 }
export const IMAGE_URI = import.meta.env.VITE_API_IMAGE_URL
export const INVESTOR_TABLE_PAGINATION = { page: 1, size: 500 }

View File

@@ -6,7 +6,7 @@ import { v4 as uuidv4 } from "uuid";
import { TbClock2 } from "react-icons/tb";
import { CiWallet } from "react-icons/ci";
import { HiOutlineReceiptPercent } from "react-icons/hi2";
import { IoMdQrScanner } from "react-icons/io";
import { IoMdQrScanner } from "react-icons/io";
import { GrDocumentPdf } from "react-icons/gr";
import { AiOutlineFileGif } from "react-icons/ai";
@@ -519,7 +519,7 @@ const GlobalStateProvider = ({ children }) => {
rate: 2.66,
},
]);
const [InvestorDetails, setInvestorDetails] = useState([
const [InvestorDetails, setInvestorDetails] = useState([
{
id: 1,
clientId: "SA00000001",
@@ -530,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",
@@ -549,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",
@@ -568,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",
@@ -587,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",
@@ -606,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",
@@ -625,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",
@@ -644,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",
@@ -663,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",
@@ -682,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",
@@ -701,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",
@@ -712,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([
{
@@ -1090,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([
@@ -1310,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,
@@ -1449,7 +1135,6 @@ const GlobalStateProvider = ({ children }) => {
},
]);
const [depositRequest, setDepositRequest] = useState([
{
id: 1,
@@ -1683,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([
@@ -1723,15 +1430,328 @@ const GlobalStateProvider = ({ children }) => {
]);
const [users, setUsers] = useState([
{
id: 1,
firstName: "SA00000001",
lastName: "John David",
emailID: "John",
role: "David",
phoneNumber:"8940035906",
},
]);
const [fawateerRequest, setFawateerRequest] = useState([
{
id: 8,
principal_xid: 2,
transaction_date: "2024-10-07",
transaction_amount: "2000.00",
transactionStatus: "Approved",
makerComment: "This is a sample comment for the transactio",
checkerComment: "This is a sample comment for the transaction",
spportFile_path: "public\\spportFile_path\\screenshot_2024_02_15_152810.png",
clientReference_id: "BH00000001",
firstName: "jayesh",
lastName: "jain ",
mobileNumber: "+919819906537",
emailAddress: "jayeshkjain6@gmail.com",
maker: {
firstName: "Faisal",
lastName: "Aljalahma",
emailAddress: "admin@tanami.com",
ISDcode: "+973",
mobileNumber: "3633133"
}
},
{
id: 8,
principal_xid: 2,
transaction_date: "2024-10-07",
transaction_amount: "2000.00",
transactionStatus: "Approved",
makerComment: "This is a sample comment for the transactio",
checkerComment: "This is a sample comment for the transaction",
spportFile_path: "public\\spportFile_path\\screenshot_2024_02_15_152810.png",
clientReference_id: "BH00000001",
firstName: "jayesh",
lastName: "jain ",
mobileNumber: "+919819906537",
emailAddress: "jayeshkjain6@gmail.com",
maker: {
firstName: "Faisal",
lastName: "Aljalahma",
emailAddress: "admin@tanami.com",
ISDcode: "+973",
mobileNumber: "3633133"
}
},
{
id: 8,
principal_xid: 2,
transaction_date: "2024-10-07",
transaction_amount: "2000.00",
transactionStatus: "Approved",
makerComment: "This is a sample comment for the transactio",
checkerComment: "This is a sample comment for the transaction",
spportFile_path: "public\\spportFile_path\\screenshot_2024_02_15_152810.png",
clientReference_id: "BH00000001",
firstName: "jayesh",
lastName: "jain ",
mobileNumber: "+919819906537",
emailAddress: "jayeshkjain6@gmail.com",
maker: {
firstName: "Faisal",
lastName: "Aljalahma",
emailAddress: "admin@tanami.com",
ISDcode: "+973",
mobileNumber: "3633133"
}
},
{
id: 8,
principal_xid: 2,
transaction_date: "2024-10-07",
transaction_amount: "2000.00",
transactionStatus: "Approved",
makerComment: "This is a sample comment for the transactio",
checkerComment: "This is a sample comment for the transaction",
spportFile_path: "public\\spportFile_path\\screenshot_2024_02_15_152810.png",
clientReference_id: "BH00000001",
firstName: "jayesh",
lastName: "jain ",
mobileNumber: "+919819906537",
emailAddress: "jayeshkjain6@gmail.com",
maker: {
firstName: "Faisal",
lastName: "Aljalahma",
emailAddress: "admin@tanami.com",
ISDcode: "+973",
mobileNumber: "3633133"
}
},
]);
const [approveHistory, setApproveHistory] = useState([
{
"id": 1,
"principal_xid": 2,
"transaction_date": "2024-10-07",
"transaction_amount": "2000.00",
"transactionStatus": "Pending",
"makerComment": null,
"checkerComment": null,
"spportFile_path": null,
"clientReference_id": "BH00000001",
"firstName": "jayesh",
"lastName": "jain",
"mobileNumber": "+919819906537",
"emailAddress": "jayeshkjain6@gmail.com",
"maker": {
"firstName": "Faisal",
"lastName": "Aljalahma",
"emailAddress": "admin@tanami.com",
"ISDcode": "+973",
"mobileNumber": "3633133"
}
},
]);
const [approved, setApproved] = useState([
{
id: 1,
transactionDate: "02-Jan-24",
particulars: "Cash Reserve- Initated",
amount: "50,000.00",
Comments: "",
user: "Faisal",
entryDate: "02-Jan-24",
},
{
id: 2,
transactionDate: "12-Feb-24",
particulars: "Fees & Expense",
amount: "-22,000.00",
Comments: "",
user: "Faisal",
entryDate: "13-Feb-24",
},
{
id: 3,
transactionDate: "12-Feb-24",
particulars: "Distribution Received From Sponsor",
amount: "50,000.00",
Comments: "",
user: "Nawab",
entryDate: "24-Mar-24",
},
{
id: 4,
transactionDate: "28-Mar-24",
particulars: "Distribution Paid To Investors",
amount: "-40,000.00",
Comments: "",
user: "Faisal",
entryDate: "28-Mar-24",
},
{
id: 5,
transactionDate: "26-Jun-24",
particulars: "Distribution Received From Sponsor",
amount: "70,000.00",
Comments: "",
user: "Faisal",
entryDate: "27-Jun-24",
},
{
id: 6,
transactionDate: "28-Jun-24",
particulars: "Distribution Paid To Investors",
amount: "-60,000.00",
Comments: "",
user: "Nawab",
entryDate: "28-Jun-24",
},
]);
const [iONAVDetail, setIONAVDetail] = useState([
{
id: 1,
valuationDate: "01-Jul-24",
nav: "1,229,750.00 ",
lastUpdate: "12.56",
investmentClose: "29.45",
updatedBy: "Nawab",
updatedOn: "01-Jul-24",
},
{
id: 2,
valuationDate: "25-Apr-24",
nav: "1,092,500.00",
lastUpdate: "15.00",
investmentClose: "15.00",
updatedBy: "Faisal",
updatedOn: "25-Apr-24",
},
{
id: 3,
valuationDate: "02-Jan-24",
nav: "950,000.00",
lastUpdate: "",
investmentClose: "",
updatedBy: "Faisal",
updatedOn: "02-Jan-24",
},
]);
const [iOTransaction, setIOTransaction] = useState([
{
id: 1,
transactionName: "Amount Invested",
amount: "995,000",
createdBy: "Faisal",
createdOn: "27-Oct-24",
approvedBy: "Nawab",
approvedOn: "28-Oct-24",
},
{
id: 2,
transactionName: "Distribution To Sponser",
amount: "40,000",
createdBy: "Faisal",
createdOn: "30-Oct-24",
approvedBy: "Nawab",
approvedOn: "31-Oct-24",
},
{
id: 3,
transactionName: "Amount Invested",
amount: "995,000",
createdBy: "Faisal",
createdOn: "27-Oct-24",
approvedBy: "Nawab",
approvedOn: "28-Oct-24",
},
]);
const [opportunities, setOpportunities] = useState([
{
id: 1,
Investors: "Blackstone",
amountRemaining: "10 %",
totalDealsize: "$ 520",
noViews: "50",
},
{
id: 2,
Investors: "Blackstone",
amountRemaining: "10 %",
totalDealsize: "$ 520",
noViews: "50",
},
{
id: 3,
Investors: "Blackstone",
amountRemaining: "10 %",
totalDealsize: "$ 520",
noViews: "50",
},
{
id: 4,
Investors: "Blackstone",
amountRemaining: "10 %",
totalDealsize: "$ 520",
noViews: "50",
},
{
id: 5,
Investors: "Blackstone",
amountRemaining: "10 %",
totalDealsize: "$ 520",
noViews: "50",
},
{
id: 6,
Investors: "Blackstone",
amountRemaining: "10 %",
totalDealsize: "$ 520",
noViews: "50",
},
{
id: 7,
Investors: "Blackstone",
amountRemaining: "10 %",
totalDealsize: "$ 520",
noViews: "50",
},
{
id: 8,
Investors: "Blackstone",
amountRemaining: "10 %",
totalDealsize: "$ 520",
noViews: "50",
},
{
id: 9,
Investors: "Blackstone",
amountRemaining: "10 %",
totalDealsize: "$ 520",
noViews: "50",
},
{
id: 10,
Investors: "Blackstone",
amountRemaining: "10 %",
totalDealsize: "$ 520",
noViews: "50",
},
]);
const [InvestorWallet, setInvestorWallet] = useState(null);
// ==============[ prod state ]===============================
const [IODetails, setIODetails] = useState(null);
const [ isIOloading, setIOloading ] = useState(false)
const [isIOloading, setIOloading] = useState(false);
return (
<GlobalStateContext.Provider
@@ -1804,10 +1824,24 @@ const GlobalStateProvider = ({ children }) => {
setAcademicDocuments,
iOArtifactsTwo,
setIOArtifactsTwo,
isIOloading,
setIOloading
InvestorWallet,
setInvestorWallet,
isIOloading,
setIOloading,
users,
setUsers,
fawateerRequest,
setFawateerRequest,
approveHistory,
setApproveHistory,
approved,
setApproved,
iONAVDetail,
setIONAVDetail,
iOTransaction,
setIOTransaction,
opportunities,
setOpportunities,
}}
>
{children}

View File

@@ -0,0 +1,4 @@
<svg width="25" height="22" viewBox="0 0 25 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.21972 0.088562C3.15668 0.321268 2.26287 0.834278 1.46427 1.66461C-0.111786 3.31471 -0.450268 5.80044 0.618065 7.85248C1.32676 9.20112 2.53789 10.1637 4.01346 10.5498L4.53705 10.6873V15.0293C4.53705 19.8104 4.52647 19.6306 4.86495 20.334C5.20872 21.0427 6.02848 21.6985 6.84295 21.91C7.29779 22.0264 16.9974 22.0317 17.4575 21.91C18.5417 21.6297 19.4091 20.7624 19.6894 19.6782C19.7581 19.4243 19.7687 18.6998 19.7687 15.0241V10.6714L19.9168 10.6397C21.6832 10.2483 22.9684 9.28045 23.7353 7.76257C24.7349 5.77928 24.3911 3.32 22.8891 1.7175C22.6722 1.4848 22.3391 1.17805 22.1487 1.03525C21.715 0.702057 20.8741 0.289536 20.324 0.13616L19.9009 0.0145187L12.2851 0.00394058C5.08708 -0.00663567 4.64282 -0.0013485 4.21972 0.088562ZM19.7687 1.80212C19.9591 1.85501 20.3135 1.99252 20.562 2.11416C20.9217 2.28869 21.0962 2.41562 21.4664 2.78584C21.8525 3.1772 21.9636 3.32529 22.1645 3.7431C22.466 4.38304 22.5453 4.70037 22.54 5.35618C22.54 6.77357 21.7732 7.98999 20.4827 8.61406C20.25 8.72513 19.9961 8.8309 19.9168 8.85206L19.7687 8.88379V6.96925C19.7687 5.11289 19.7634 5.04414 19.6576 4.86961C19.5942 4.77441 19.4567 4.64748 19.3403 4.58931C19.1446 4.48353 18.9966 4.48353 12.1529 4.48353C5.30921 4.48353 5.16112 4.48353 4.96544 4.58931C4.84908 4.64748 4.71158 4.77441 4.64811 4.86961C4.54233 5.04414 4.53705 5.11289 4.53705 6.96925V8.88379L4.39425 8.85206C4.12981 8.78859 3.40525 8.4078 3.10379 8.16452C2.56433 7.73613 2.13594 7.11734 1.90853 6.41393C1.79746 6.07016 1.77102 5.87977 1.76573 5.40907C1.76044 4.70566 1.82919 4.39891 2.14123 3.74839C2.33692 3.34116 2.45327 3.1772 2.80762 2.81757C3.44227 2.18291 4.12981 1.83914 4.97601 1.73866C5.18756 1.71221 8.52478 1.69635 12.3909 1.70164C18.9014 1.71221 19.4514 1.7175 19.7687 1.80212ZM18.0657 12.6917L18.0499 19.1599L17.9018 19.4613C17.7378 19.7998 17.5316 19.9955 17.1772 20.1595C16.9392 20.2705 16.8969 20.2705 12.1529 20.2705C7.40885 20.2705 7.36654 20.2705 7.12854 20.1595C6.7742 19.9955 6.56793 19.7998 6.40398 19.4613L6.2559 19.1599L6.24003 12.6917L6.22416 6.22883H12.1529H18.0816L18.0657 12.6917Z" fill="#004717"/>
<path d="M11.6239 9.05782C11.1585 9.19004 10.9576 9.34341 10.0003 10.3113C9.23342 11.0834 9.09062 11.2579 9.05889 11.4272C8.95312 11.9984 9.27044 12.4585 9.81519 12.5114C10.1642 12.5431 10.3388 12.4479 10.8729 11.9243L11.3066 11.4907V13.9817V16.4727L11.4283 16.6472C11.7615 17.1391 12.5442 17.1391 12.8827 16.6472L12.999 16.4727V13.9817V11.4907L13.438 11.9243C13.9669 12.4479 14.1414 12.5431 14.4905 12.5114C14.9136 12.4691 15.215 12.1782 15.2573 11.7657C15.3049 11.3426 15.2309 11.2315 14.1996 10.2108C13.3375 9.35928 13.2053 9.24821 12.9197 9.14244C12.5072 8.98906 11.9994 8.95733 11.6239 9.05782Z" fill="#004717"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,4 @@
<svg width="25" height="22" viewBox="0 0 25 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.21995 0.0885715C3.15685 0.321289 2.263 0.834328 1.46435 1.66471C-0.111792 3.3149 -0.450292 5.80076 0.618099 7.85292C1.32683 9.20163 2.53803 10.1642 4.01368 10.5503L4.5373 10.6879V15.0302C4.5373 19.8115 4.52672 19.6317 4.86522 20.3351C5.20901 21.0438 6.02881 21.6997 6.84333 21.9113C7.29819 22.0276 16.9983 22.0329 17.4585 21.9113C18.5427 21.6309 19.4102 20.7635 19.6905 19.6793C19.7592 19.4254 19.7698 18.7008 19.7698 15.0249V10.672L19.9179 10.6403C21.6845 10.2489 22.9697 9.28097 23.7366 7.763C24.7362 5.7796 24.3925 3.32019 22.8904 1.7176C22.6735 1.48488 22.3403 1.17812 22.1499 1.03531C21.7162 0.702101 20.8752 0.289555 20.3252 0.136171L19.902 0.0145245L12.2858 0.0039463C5.08736 -0.00663185 4.64308 -0.00134277 4.21995 0.0885715ZM19.7698 1.80223C19.9602 1.85512 20.3146 1.99263 20.5632 2.11428C20.9228 2.28882 21.0974 2.41576 21.4676 2.78599C21.8537 3.17738 21.9648 3.32548 22.1658 3.74331C22.4672 4.38329 22.5466 4.70063 22.5413 5.35648C22.5413 6.77395 21.7744 7.99043 20.4838 8.61454C20.2511 8.72561 19.9972 8.8314 19.9179 8.85255L19.7698 8.88429V6.96964C19.7698 5.11318 19.7645 5.04442 19.6587 4.86988C19.5953 4.77468 19.4578 4.64774 19.3414 4.58956C19.1457 4.48378 18.9976 4.48378 12.1536 4.48378C5.3095 4.48378 5.16141 4.48378 4.96571 4.58956C4.84935 4.64774 4.71184 4.77468 4.64837 4.86988C4.54259 5.04442 4.5373 5.11318 4.5373 6.96964V8.88429L4.39449 8.85255C4.13004 8.78908 3.40544 8.40827 3.10396 8.16497C2.56448 7.73656 2.13606 7.11774 1.90863 6.41429C1.79756 6.0705 1.77112 5.8801 1.76583 5.40937C1.76054 4.70592 1.8293 4.39916 2.14135 3.7486C2.33705 3.34134 2.4534 3.17738 2.80777 2.81773C3.44246 2.18304 4.13004 1.83925 4.97629 1.73876C5.18785 1.71231 8.52525 1.69645 12.3916 1.70173C18.9024 1.71231 19.4525 1.7176 19.7698 1.80223ZM18.0667 12.6924L18.0509 19.1609L17.9028 19.4624C17.7388 19.8009 17.5325 19.9966 17.1782 20.1606C16.9402 20.2716 16.8978 20.2716 12.1536 20.2716C7.40926 20.2716 7.36695 20.2716 7.12894 20.1606C6.77457 19.9966 6.5683 19.8009 6.40434 19.4624L6.25624 19.1609L6.24038 12.6924L6.22451 6.22917H12.1536H18.0826L18.0667 12.6924Z" fill="#004717"/>
<path d="M11.8362 9.03725C11.7621 9.06369 11.6458 9.14303 11.5823 9.2065C11.302 9.46566 11.3073 9.42864 11.3073 12.0838V14.559L10.8471 14.0989C10.588 13.8503 10.3288 13.6176 10.2653 13.5858C10.0538 13.4801 9.60421 13.5118 9.39793 13.6546C9.04357 13.8926 8.9325 14.3898 9.13348 14.7865C9.19166 14.8922 9.62536 15.363 10.1014 15.8337C11.1909 16.9074 11.339 16.992 12.1535 16.992C12.9628 16.992 13.1109 16.9074 14.211 15.8337C14.6817 15.363 15.1207 14.8922 15.1736 14.7865C15.3746 14.3898 15.2424 13.8609 14.888 13.6387C14.687 13.5171 14.2374 13.4854 14.0417 13.5858C13.9783 13.6176 13.7191 13.8503 13.4652 14.0989L12.9998 14.559V12.0679V9.57673L12.8834 9.40219C12.8147 9.30699 12.6983 9.18005 12.619 9.12187C12.455 9.00023 12.0319 8.95262 11.8362 9.03725Z" fill="#004717"/>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,7 @@
<svg width="27" height="27" viewBox="0 0 27 27" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.85 4.90458V4.91276C0.85 6.01672 0.856512 6.94052 0.867926 7.59765C0.873631 7.92606 0.880574 8.18879 0.888592 8.37443C0.892591 8.46702 0.89692 8.54198 0.901643 8.5969C0.903985 8.62414 0.906596 8.64852 0.909645 8.66854C0.911156 8.67847 0.913075 8.68934 0.915626 8.69997C0.917334 8.70708 0.921735 8.72497 0.931566 8.7441M0.85 4.90458L8.67578 1.97656M0.85 4.90458C0.85 3.12796 0.85 2.21329 0.866361 1.72737C0.874474 1.48641 0.886798 1.33911 0.908538 1.24176C0.919918 1.1908 0.935053 1.14765 0.957087 1.1092C0.979442 1.0702 1.00475 1.04353 1.02414 1.02414C1.04354 1.00474 1.0702 0.979438 1.10921 0.957083C1.14765 0.935049 1.19081 0.919914 1.24176 0.908534C1.33911 0.886794 1.48641 0.874471 1.72737 0.866357C2.21328 0.849997 3.1279 0.849997 4.90439 0.849997H4.91276C6.01672 0.849997 6.94052 0.856508 7.59766 0.867923C7.92606 0.873627 8.18879 0.88057 8.37443 0.888588C8.46702 0.892587 8.54198 0.896916 8.5969 0.901639C8.62414 0.903981 8.64852 0.906592 8.66855 0.909641C8.67847 0.911153 8.68935 0.913072 8.69997 0.915623C8.70708 0.91733 8.72499 0.921734 8.74413 0.931574M0.85 4.90458L8.67578 1.0651M0.931566 8.7441C0.932164 8.74527 0.932781 8.74643 0.93342 8.7476L1.0651 8.67578M0.931566 8.7441C0.931356 8.74369 0.931148 8.74327 0.93094 8.74286L1.0651 8.67578M0.931566 8.7441C0.963258 8.80668 1.02187 8.86203 1.07407 8.90253C1.13034 8.94619 1.19851 8.9874 1.26743 9.0159C1.41953 9.08075 1.59317 9.07817 1.74168 9.03232C1.88916 8.98679 2.03374 8.89141 2.10946 8.74534C2.11906 8.72705 2.12362 8.70995 2.12556 8.70234C2.1282 8.69195 2.13019 8.68143 2.13175 8.67193C2.13489 8.65279 2.13755 8.62989 2.13992 8.60472C2.14469 8.55396 2.14904 8.48537 2.15304 8.40135C2.16108 8.23282 2.16803 7.99558 2.17374 7.70049C2.18515 7.10996 2.19167 6.2837 2.19167 5.30338V2.19166H5.30339C6.28371 2.19166 7.10996 2.18515 7.70049 2.17373C7.99558 2.16803 8.23282 2.16108 8.40135 2.15304C8.48538 2.14903 8.55396 2.14468 8.60472 2.13991C8.62989 2.13755 8.6528 2.13489 8.67193 2.13175C8.68144 2.13019 8.69195 2.1282 8.70234 2.12555C8.70994 2.12362 8.72702 2.11907 8.74528 2.10949M1.0651 8.67578C1.02604 8.60416 1 7.11979 1 4.91276C1 1.34505 1 1.26041 1.13021 1.1302C1.26042 0.999996 1.34505 0.999996 4.91276 0.999996C7.11979 0.999996 8.60417 1.02604 8.67578 1.0651M1.0651 8.67578C1.09766 8.74088 1.21484 8.83203 1.32552 8.8776L8.67578 1.0651M8.74528 2.10949C8.74606 2.10908 8.74683 2.10867 8.74761 2.10824L8.67578 1.97656M8.74528 2.10949C8.74505 2.10961 8.74481 2.10973 8.74458 2.10985L8.67578 1.97656M8.74528 2.10949C8.89139 2.03377 8.98678 1.88918 9.03232 1.74167C9.07817 1.59316 9.08075 1.41951 9.0159 1.26741C8.9874 1.1985 8.94619 1.13034 8.90253 1.07406C8.86204 1.02187 8.80669 0.963269 8.74413 0.931574M8.67578 1.97656C8.8776 1.87239 8.97526 1.55338 8.8776 1.32552C8.83203 1.21484 8.74089 1.09765 8.67578 1.0651M8.74413 0.931574C8.74529 0.932168 8.74645 0.932782 8.74761 0.933416L8.67578 1.0651M8.74413 0.931574C8.74371 0.93136 8.74329 0.931148 8.74286 0.930936L8.67578 1.0651" fill="#004717" stroke="#004717" stroke-width="0.3"/>
<path d="M17.9845 1.22324C17.9499 1.311 17.9398 1.4096 17.9398 1.52083C17.9398 1.63206 17.9499 1.73066 17.9845 1.81842C18.0208 1.91027 18.0788 1.9776 18.1526 2.03525C18.2078 2.08018 18.2679 2.11228 18.3744 2.13377C18.4723 2.15351 18.6152 2.16551 18.8404 2.17366C19.2939 2.19006 20.1195 2.19166 21.6771 2.19166H24.8083V5.30338C24.8083 6.2837 24.8148 7.10996 24.8263 7.70049C24.832 7.99558 24.8389 8.23282 24.847 8.40135C24.851 8.48537 24.8553 8.55396 24.8601 8.60472C24.8624 8.62989 24.8651 8.65279 24.8682 8.67193C24.8698 8.68143 24.8718 8.69195 24.8744 8.70234C24.8764 8.70994 24.8809 8.72703 24.8905 8.7453M17.9845 1.22324L25.0234 8.67578M17.9845 1.22324C18.0208 1.13139 18.0788 1.06406 18.1526 1.0064L17.9845 1.22324ZM24.8905 8.7453C24.8909 8.74607 24.8913 8.74684 24.8918 8.7476L25.0234 8.67578M24.8905 8.7453C24.8904 8.74506 24.8903 8.74482 24.8901 8.74457L25.0234 8.67578M24.8905 8.7453C24.9662 8.89139 25.1108 8.98678 25.2583 9.03232C25.4068 9.07817 25.5805 9.08075 25.7326 9.01589M25.0234 8.67578C25.1276 8.8776 25.4466 8.97526 25.6745 8.8776M25.7326 9.01589C25.7323 9.01603 25.7319 9.01617 25.7316 9.0163L25.6745 8.8776M25.7326 9.01589C25.7329 9.01575 25.7332 9.01561 25.7336 9.01547L25.6745 8.8776M25.7326 9.01589L25.6745 8.8776M26.0684 8.74412C26.0678 8.74528 26.0672 8.74644 26.0666 8.7476L25.9349 8.67578L26.0691 8.74286C26.0688 8.74328 26.0686 8.7437 26.0684 8.74412Z" fill="#004717" stroke="#004717" stroke-width="0.3"/>
<path d="M19.7169 20.2555C19.7561 20.1886 19.7914 20.1261 19.8187 20.0545C19.8467 19.9812 19.8645 19.9036 19.8762 19.8046C19.8986 19.6137 19.9 19.3226 19.9 18.7934C19.9 17.9063 19.8742 17.5712 19.7651 17.1482C19.3751 15.6152 18.154 14.2862 16.66 13.7815L16.6594 13.7813L16.3409 13.6752L16.6146 13.3975C17.1158 12.8895 17.5323 12.1429 17.7144 11.4214C17.7808 11.1594 17.8118 10.769 17.8118 10.3836C17.8118 9.99752 17.7807 9.59879 17.715 9.31905L17.715 9.319C17.2151 7.19077 15.1037 5.77026 12.9072 6.06661C11.167 6.29605 9.73096 7.57041 9.29241 9.27619C9.22583 9.52546 9.19154 9.90933 9.18579 10.2877C9.18002 10.6675 9.20276 11.0608 9.25801 11.3301L9.25806 11.3303C9.30092 11.5375 9.47027 11.9741 9.6273 12.3078L9.62808 12.3095L9.62809 12.3095C9.85584 12.7784 10.0341 13.0394 10.3851 13.3972L10.3853 13.3974L10.6591 13.6752L10.3406 13.7813L10.34 13.7815C8.84605 14.2862 7.62497 15.6151 7.2349 17.1481C7.12575 17.5711 7.1 17.9063 7.1 18.7934C7.1 19.3226 7.10136 19.6137 7.12381 19.8046C7.13547 19.9036 7.15326 19.9812 7.18129 20.0545C7.20864 20.1261 7.24394 20.1886 7.28308 20.2555C7.39392 20.4474 7.5949 20.6484 7.78679 20.7591C7.78687 20.7592 7.78694 20.7592 7.78702 20.7593L8.06653 20.9218L8.10149 20.9421H8.14193H13.5H18.8581H18.8985L18.9335 20.9218L19.213 20.7593C19.213 20.7593 19.2131 20.7592 19.2131 20.7592C19.4051 20.6484 19.6061 20.4474 19.7169 20.2555ZM19.7169 20.2555C19.717 20.2554 19.7171 20.2552 19.7172 20.2551L19.5872 20.1801L19.7167 20.2559C19.7168 20.2558 19.7169 20.2556 19.7169 20.2555ZM14.3606 7.58254L14.3599 7.58233C13.5947 7.34167 12.6188 7.47247 11.9597 7.89114C11.0841 8.45651 10.5901 9.35333 10.5901 10.382C10.5901 11.2305 10.8905 11.9143 11.5456 12.5319L11.5456 12.5319C12.0893 13.0448 12.703 13.2853 13.5 13.2853C14.3197 13.2853 14.9335 13.0341 15.531 12.4552L15.5316 12.4546C16.1264 11.8846 16.4099 11.2079 16.4099 10.3754C16.4099 9.55444 16.1707 8.96017 15.6117 8.37632L15.6109 8.37543C15.2047 7.94423 14.8817 7.74382 14.3606 7.58254ZM15.7673 15.0129L15.7663 15.0126C15.3468 14.9016 15.0954 14.8874 13.4746 14.8939H13.474C11.8987 14.8939 11.5948 14.9145 11.2203 15.0127L11.22 15.0128C9.94165 15.3449 8.96771 16.2766 8.60518 17.4999C8.55776 17.6733 8.49341 18.1916 8.46746 18.6457L8.46746 18.6458C8.44432 19.0491 8.43591 19.244 8.4474 19.3568C8.45269 19.4087 8.46135 19.4314 8.46841 19.4446C8.4763 19.4595 8.48841 19.4746 8.52143 19.5077L8.61422 19.6004H13.5H18.3858L18.4786 19.5077C18.5116 19.4746 18.5237 19.4595 18.5316 19.4446C18.5387 19.4314 18.5473 19.4087 18.5526 19.3568C18.5641 19.244 18.5557 19.0491 18.5325 18.6458L18.5325 18.6457C18.5066 18.1917 18.4423 17.6736 18.3949 17.5001C18.0259 16.264 17.0587 15.3452 15.7673 15.0129ZM8.41536 19.6137C8.27865 19.477 8.27214 19.4314 8.31771 18.6372L8.55208 19.7504L8.41536 19.6137Z" fill="#004717" stroke="#004717" stroke-width="0.3"/>
<path d="M1.22097 17.9849C1.36074 17.9241 1.52242 17.921 1.66423 17.9519C1.80571 17.9827 1.9487 18.052 2.04388 18.1608L2.04399 18.1607L2.04812 18.1659C2.09512 18.2246 2.12219 18.292 2.14012 18.3978C2.15724 18.4988 2.16798 18.6471 2.17531 18.8773C2.19006 19.3402 2.19167 20.1652 2.19167 21.6775V24.8088H5.30339C6.28371 24.8088 7.10996 24.8153 7.70049 24.8267C7.99558 24.8324 8.23282 24.8394 8.40135 24.8474C8.48538 24.8514 8.55396 24.8558 8.60472 24.8605C8.62989 24.8629 8.6528 24.8656 8.67193 24.8687C8.68144 24.8703 8.69195 24.8722 8.70234 24.8749C8.70995 24.8768 8.72705 24.8814 8.74534 24.891C8.89141 24.9667 8.98679 25.1113 9.03232 25.2588C9.07817 25.4073 9.08075 25.5809 9.01589 25.733M1.22097 17.9849C1.16165 18.0099 1.10541 18.0503 1.05999 18.0918C1.01471 18.1332 0.970638 18.1842 0.940974 18.2379C0.924837 18.2663 0.918305 18.2965 0.915428 18.3102C0.911126 18.3307 0.907629 18.3544 0.90462 18.3799C0.898557 18.4312 0.893319 18.4997 0.888679 18.5839C0.879369 18.753 0.872006 18.9945 0.866301 19.3083C0.854881 19.9363 0.85 20.8604 0.85 22.0877V22.0959C0.85 23.8725 0.85 24.7871 0.866361 25.2731C0.874474 25.514 0.886798 25.6613 0.908538 25.7587C0.919918 25.8096 0.935053 25.8528 0.957087 25.8912C0.979442 25.9302 1.00475 25.9569 1.02414 25.9763C1.04354 25.9957 1.0702 26.021 1.10921 26.0434C1.14765 26.0654 1.19081 26.0805 1.24176 26.0919C1.33911 26.1136 1.48641 26.126 1.72737 26.1341C2.2133 26.1504 3.12797 26.1504 4.90458 26.1504H4.91276C6.01672 26.1504 6.94052 26.1439 7.59766 26.1325C7.92606 26.1268 8.18879 26.1199 8.37443 26.1119C8.46702 26.1079 8.54198 26.1035 8.5969 26.0988C8.62414 26.0965 8.64852 26.0938 8.66855 26.0908C8.67847 26.0893 8.68935 26.0874 8.69997 26.0848C8.70711 26.0831 8.72509 26.0787 8.7443 26.0688C8.80679 26.0371 8.86207 25.9785 8.90253 25.9264C8.94619 25.8701 8.98739 25.8019 9.01589 25.733M1.22097 17.9849C1.22059 17.9851 1.22021 17.9852 1.21982 17.9854L1.27995 18.1228L1.22226 17.9844C1.22183 17.9846 1.2214 17.9847 1.22097 17.9849ZM9.01589 25.733C9.01603 25.7327 9.01617 25.7324 9.01631 25.732L8.87761 25.6749L9.01548 25.734C9.01562 25.7337 9.01575 25.7334 9.01589 25.733Z" fill="#004717" stroke="#004717" stroke-width="0.3"/>
<path d="M24.8979 18.2405L24.8983 18.2398C24.9279 18.1854 24.9725 18.1336 25.0183 18.0918C25.0637 18.0503 25.12 18.0099 25.1793 17.9849C25.3191 17.9241 25.4808 17.921 25.6226 17.9519C25.764 17.9827 25.907 18.052 26.0022 18.1608L26.0023 18.1607L26.0065 18.1659C26.0549 18.2264 26.0813 18.2968 26.0988 18.4085C26.1156 18.5165 26.1263 18.6783 26.1337 18.934C26.1484 19.4478 26.15 20.3702 26.15 22.0682V22.0766C26.15 23.8628 26.15 24.7823 26.1336 25.2706C26.1255 25.5128 26.1132 25.6607 26.0915 25.7583C26.0801 25.8093 26.065 25.8526 26.043 25.8911C26.0206 25.9302 25.9953 25.9569 25.9759 25.9763C25.9565 25.9957 25.9298 26.021 25.8907 26.0434C25.8521 26.0654 25.8089 26.0806 25.7578 26.0919C25.6602 26.1136 25.5123 26.126 25.2702 26.1341C24.7818 26.1504 23.8623 26.1504 22.0759 26.1504H22.0677C20.3179 26.1504 19.395 26.1488 18.8923 26.1325C18.6424 26.1243 18.4873 26.1124 18.3835 26.093C18.2711 26.0719 18.2095 26.0403 18.1526 25.994C18.0788 25.9364 18.0208 25.869 17.9845 25.7772C17.9499 25.6894 17.9398 25.5908 17.9398 25.4796C17.9398 25.3684 17.9499 25.2698 17.9845 25.182C18.0208 25.0902 18.0788 25.0228 18.1526 24.9652C18.2078 24.9203 18.2679 24.8882 18.3744 24.8667C18.4723 24.8469 18.6152 24.8349 18.8404 24.8268C19.2939 24.8104 20.1195 24.8088 21.6771 24.8088H24.8083V21.6971C24.8083 20.6062 24.8148 19.7797 24.8271 19.215C24.8332 18.9328 24.8408 18.7146 24.8498 18.5608C24.8543 18.4842 24.8592 18.4216 24.8648 18.3744C24.8676 18.3509 24.8708 18.329 24.8746 18.31C24.8774 18.2958 24.8836 18.2671 24.8979 18.2405Z" fill="#004717" stroke="#004717" stroke-width="0.3"/>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

BIN
src/Images/dash_icon_3n.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,3 @@
<svg width="25" height="26" viewBox="0 0 25 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24.125 15.8268L22.8695 14.5725C22.6775 14.379 22.4489 14.2255 22.197 14.1211C21.9452 14.0167 21.6751 13.9635 21.4024 13.9644C21.1293 13.9639 20.8588 14.0177 20.6067 14.1227C20.3546 14.2278 20.126 14.382 19.9341 14.5763L12.9775 21.5646C12.8894 21.6525 12.8195 21.7569 12.7718 21.872C12.7242 21.987 12.6998 22.1103 12.7001 22.2348V25.0498C12.7001 25.3018 12.8002 25.5435 12.9784 25.7217C13.1566 25.8999 13.3982 26 13.6502 26H16.4666C16.7178 25.9996 16.9588 25.8999 17.1368 25.7225L24.125 18.7673C24.3182 18.5743 24.4716 18.3451 24.5762 18.0928C24.6808 17.8405 24.7346 17.5701 24.7346 17.297C24.7346 17.0239 24.6808 16.7535 24.5762 16.5012C24.4716 16.2489 24.3182 16.0197 24.125 15.8268ZM21.4024 15.8648C21.426 15.8642 21.4494 15.8685 21.4712 15.8774C21.493 15.8864 21.5127 15.8997 21.5291 15.9167L22.7833 17.171C22.8001 17.1875 22.8133 17.2072 22.8222 17.229C22.8312 17.2508 22.8356 17.2741 22.8353 17.2976C22.8357 17.3212 22.8314 17.3446 22.8224 17.3664C22.8135 17.3882 22.8002 17.4079 22.7833 17.4243L21.9776 18.2263L20.4776 16.7263L21.2795 15.9205C21.2953 15.9036 21.3143 15.89 21.3354 15.8804C21.3565 15.8708 21.3793 15.8655 21.4024 15.8648ZM16.0713 24.0996H14.5979V22.6262L19.1347 18.0654L20.6283 19.5591L16.0713 24.0996ZM9.84699 22.8327H4.77939C2.78148 22.8327 1.92886 21.9801 1.92886 19.9822V4.77939C1.92886 2.78148 2.78148 1.92886 4.77939 1.92886H11.4306V4.77939C11.3562 5.42127 11.4279 6.07168 11.6404 6.68194C11.8528 7.2922 12.2005 7.84653 12.6574 8.30345C13.1144 8.76037 13.6687 9.10806 14.279 9.32051C14.8892 9.53296 15.5396 9.60467 16.1815 9.53027H19.032V11.1139C19.032 11.3659 19.1321 11.6076 19.3103 11.7858C19.4885 11.964 19.7302 12.0641 19.9822 12.0641C20.2342 12.0641 20.4759 11.964 20.6541 11.7858C20.8323 11.6076 20.9324 11.3659 20.9324 11.1139V8.58009C20.9325 8.45532 20.9079 8.33177 20.8601 8.21653C20.8122 8.1013 20.7421 7.99666 20.6537 7.90863L13.0523 0.30722C12.9642 0.218794 12.8596 0.148646 12.7444 0.100813C12.6291 0.0529792 12.5056 0.0284043 12.3808 0.028502H4.77939C4.1375 -0.0458957 3.4871 0.0258118 2.87683 0.23826C2.26657 0.450709 1.71224 0.798399 1.25532 1.25532C0.798399 1.71224 0.450709 2.26657 0.23826 2.87683C0.0258118 3.4871 -0.0458957 4.1375 0.028502 4.77939V19.9822C-0.0458957 20.6241 0.0258118 21.2745 0.23826 21.8848C0.450709 22.495 0.798399 23.0494 1.25532 23.5063C1.71224 23.9632 2.26657 24.3109 2.87683 24.5233C3.4871 24.7358 4.1375 24.8075 4.77939 24.7331H9.84699C10.099 24.7331 10.3407 24.633 10.5189 24.4548C10.6971 24.2766 10.7972 24.0349 10.7972 23.7829C10.7972 23.5309 10.6971 23.2892 10.5189 23.111C10.3407 22.9328 10.099 22.8327 9.84699 22.8327ZM13.331 4.77939V3.27304L17.6879 7.62992H16.1815C14.1836 7.62992 13.331 6.77729 13.331 4.77939ZM6.04629 11.4306C5.79429 11.4306 5.5526 11.5307 5.37441 11.7089C5.19622 11.8871 5.09611 12.1288 5.09611 12.3808C5.09611 12.6328 5.19622 12.8745 5.37441 13.0527C5.5526 13.2309 5.79429 13.331 6.04629 13.331H14.9146C15.1666 13.331 15.4083 13.2309 15.5865 13.0527C15.7647 12.8745 15.8648 12.6328 15.8648 12.3808C15.8648 12.1288 15.7647 11.8871 15.5865 11.7089C15.4083 11.5307 15.1666 11.4306 14.9146 11.4306H6.04629ZM11.1139 16.4982H6.04629C5.79429 16.4982 5.5526 16.5983 5.37441 16.7765C5.19622 16.9547 5.09611 17.1964 5.09611 17.4484C5.09611 17.7004 5.19622 17.9421 5.37441 18.1203C5.5526 18.2985 5.79429 18.3986 6.04629 18.3986H11.1139C11.3659 18.3986 11.6076 18.2985 11.7858 18.1203C11.964 17.9421 12.0641 17.7004 12.0641 17.4484C12.0641 17.1964 11.964 16.9547 11.7858 16.7765C11.6076 16.5983 11.3659 16.4982 11.1139 16.4982Z" fill="#004717"/>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,3 @@
<svg width="36" height="24" viewBox="0 0 36 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.5257 11.8995C11.9061 11.3689 11.1971 10.946 10.4241 10.6563C11.3784 9.91683 12.0006 8.75408 12.0006 7.45123C12.0006 5.20903 10.187 3.39548 7.94484 3.39548C5.70264 3.39548 3.88909 5.20903 3.88909 7.45123C3.88909 8.75391 4.50502 9.91698 5.4651 10.6567C2.79743 11.6588 0.9 14.2336 0.9 17.2481V17.7005C0.9 17.7399 0.918683 17.7709 0.937861 17.7901C0.957039 17.8093 0.988047 17.828 1.02742 17.828H9.29909C9.25911 18.1738 9.23655 18.53 9.23655 18.8866V19.3528C9.23655 21.4236 10.913 23.1 12.9838 23.1H23.6102C25.681 23.1 27.3574 21.4236 27.3574 19.3528V18.8866C27.3574 18.53 27.3348 18.1738 27.2949 17.828H35.6214C35.6607 17.828 35.6918 17.8093 35.7109 17.7901C35.7301 17.7709 35.7488 17.7399 35.7488 17.7005L35.7488 17.2481L35.7488 17.2476C35.7352 14.2268 33.8442 11.6517 31.1762 10.6496C32.1306 9.9101 32.7528 8.74729 32.7528 7.44438C32.7528 5.20217 30.9393 3.38863 28.6971 3.38863C26.4549 3.38863 24.6413 5.20217 24.6413 7.44438C24.6413 8.74713 25.2573 9.91024 26.2175 10.65C25.4315 10.9457 24.7099 11.3755 24.0884 11.9192C23.2212 11.1947 22.2146 10.6295 21.117 10.2719C22.4903 9.35643 23.4016 7.78683 23.4016 6.01153C23.4016 3.18659 21.1151 0.9 18.2901 0.9C15.4651 0.9 13.1786 3.19354 13.1786 6.01153C13.1786 7.78699 14.0838 9.35703 15.4635 10.2726C14.3792 10.6294 13.3856 11.1879 12.5257 11.8995ZM25.9383 7.43752C25.9383 5.91593 27.1755 4.67868 28.6971 4.67868C30.2187 4.67868 31.4559 5.91593 31.4559 7.43752C31.4559 8.93853 30.2459 10.1627 28.7508 10.1964H28.6431C27.1419 10.1694 25.9383 8.94585 25.9383 7.43752ZM14.4618 6.01153C14.4618 3.90721 16.1721 2.19692 18.2764 2.19692C20.3807 2.19692 22.091 3.90721 22.091 6.01153C22.091 8.04131 20.4958 9.70433 18.4998 9.81929H18.053C16.057 9.70433 14.4618 8.04131 14.4618 6.01153ZM5.16544 7.43752C5.16544 5.91593 6.40269 4.67868 7.92427 4.67868C9.44586 4.67868 10.6831 5.91593 10.6831 7.43752C10.6831 8.93853 9.47305 10.1627 7.97802 10.1964H7.87029C6.37576 10.1694 5.16544 8.94569 5.16544 7.43752ZM11.5575 12.7948C10.6118 13.8433 9.90437 15.119 9.5269 16.5242H2.22283C2.57581 13.7067 4.97707 11.5134 7.88341 11.4933H7.96532C9.3291 11.4999 10.5782 11.9915 11.5575 12.7948ZM26.0468 19.3528C26.0468 20.703 24.9466 21.8031 23.5964 21.8031H12.9701C11.6199 21.8031 10.5198 20.703 10.5198 19.3528V18.8866C10.5198 14.6853 13.874 11.248 18.0473 11.1232C18.1255 11.1299 18.209 11.1299 18.2811 11.1299H18.2833H18.2855C18.3575 11.1299 18.4411 11.1299 18.5193 11.1232C22.6926 11.248 26.0468 14.6853 26.0468 18.8866V19.3528ZM34.3985 16.5242H27.0395C26.6624 15.126 25.9676 13.8694 25.0288 12.8218C26.0149 11.9992 27.2778 11.5065 28.6564 11.4933H28.738C31.6443 11.5134 34.0456 13.7067 34.3985 16.5242Z" fill="#004717" stroke="#004717" stroke-width="0.2"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,5 @@
<svg width="26" height="24" viewBox="0 0 26 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.88412 15.6108L6.88412 15.6108H6.88636H8.75862V15.6108L8.76086 15.6108C9.11843 15.6028 9.47392 15.6673 9.80586 15.8004C10.1378 15.9336 10.4393 16.1326 10.6922 16.3856C10.9451 16.6385 11.1442 16.94 11.2773 17.2719C11.4105 17.6039 11.475 17.9594 11.467 18.3169H11.467V18.3192V18.5532C11.467 18.7129 11.4036 18.866 11.2906 18.979C11.1777 19.0919 11.0246 19.1553 10.8649 19.1553H4.78008C4.62039 19.1553 4.46725 19.0919 4.35433 18.979C4.24142 18.866 4.17798 18.7129 4.17798 18.5532V18.3192H4.17801L4.17796 18.3169C4.16995 17.9594 4.23447 17.6039 4.36763 17.2719C4.50079 16.94 4.69983 16.6385 4.95274 16.3856C5.20564 16.1326 5.50717 15.9336 5.83912 15.8004C6.17106 15.6673 6.52655 15.6028 6.88412 15.6108ZM3.77206 16.0109C3.27964 16.6786 3.02281 17.4909 3.04186 18.3203V18.5532C3.04186 19.0142 3.22499 19.4563 3.55097 19.7823C3.87695 20.1083 4.31907 20.2914 4.78008 20.2914H10.8649C11.3259 20.2914 11.768 20.1083 12.094 19.7823C12.42 19.4563 12.6031 19.0142 12.6031 18.5532V18.3203C12.6222 17.4909 12.3653 16.6786 11.8729 16.0109C11.4146 15.3894 10.7763 14.9259 10.0467 14.6817C10.4535 14.2742 10.7413 13.7618 10.877 13.1999C11.0264 12.5817 10.9846 11.9326 10.7573 11.3386C10.5299 10.7446 10.1276 10.2335 9.60362 9.87302C9.07961 9.51251 8.45854 9.31949 7.82249 9.31949C7.18644 9.31949 6.56537 9.51251 6.04136 9.87302C5.51735 10.2335 5.11507 10.7446 4.88771 11.3386C4.66034 11.9326 4.61859 12.5817 4.76797 13.1999C4.90372 13.7618 5.19153 14.2742 5.59829 14.6817C4.86865 14.9259 4.23038 15.3894 3.77206 16.0109ZM7.82249 10.4621C8.2193 10.4621 8.60719 10.5798 8.93712 10.8002C9.26705 11.0207 9.5242 11.334 9.67605 11.7006C9.82791 12.0672 9.86764 12.4706 9.79022 12.8598C9.71281 13.249 9.52173 13.6065 9.24115 13.887C8.96056 14.1676 8.60308 14.3587 8.2139 14.4361C7.82472 14.5135 7.42132 14.4738 7.05472 14.322C6.68812 14.1701 6.37478 13.9129 6.15432 13.583C5.93387 13.2531 5.8162 12.8652 5.8162 12.4684C5.8162 11.9363 6.02758 11.426 6.40383 11.0497C6.78008 10.6735 7.29039 10.4621 7.82249 10.4621Z" fill="#004717" stroke="#004717" stroke-width="0.2"/>
<path d="M21.8554 6.21372L21.8555 6.2138C21.9634 6.11569 22.1049 6.06283 22.2507 6.06616C22.3965 6.06949 22.5354 6.12875 22.6387 6.23168C22.742 6.33461 22.8017 6.47332 22.8056 6.6191C22.8094 6.76487 22.757 6.90654 22.6593 7.01477L22.6559 7.01855L22.6558 7.01846L19.3796 10.2947L21.8554 6.21372ZM21.8554 6.21372L21.8521 6.21707M21.8554 6.21372L21.8521 6.21707M21.8521 6.21707L18.9787 9.09048M21.8521 6.21707L18.9787 9.09048M18.9791 10.4623L18.9788 10.4623C18.9038 10.4623 18.8296 10.4476 18.7603 10.4188C18.6912 10.3901 18.6284 10.348 18.5756 10.2949L18.9791 10.4623ZM18.9791 10.4623C19.1294 10.4616 19.2733 10.4015 19.3794 10.2949L18.9791 10.4623ZM18.9787 9.09048L17.1959 7.30766C17.0893 7.20108 16.9447 7.1412 16.794 7.1412C16.6433 7.1412 16.4987 7.20108 16.3921 7.30766C16.2855 7.41424 16.2257 7.5588 16.2257 7.70952C16.2257 7.86025 16.2855 8.00481 16.3921 8.11139L18.5754 10.2947L18.9787 9.09048ZM22.3323 14.9429C22.1817 14.9429 22.0372 15.0028 21.9307 15.1093C21.8241 15.2158 21.7643 15.3603 21.7643 15.511V21.3618C21.7643 21.5214 21.7008 21.6746 21.5879 21.7875C21.475 21.9004 21.3219 21.9639 21.1622 21.9639H1.9716C1.81191 21.9639 1.65876 21.9004 1.54585 21.7875C1.43294 21.6746 1.3695 21.5214 1.3695 21.3618V7.78793C1.3695 7.62824 1.43294 7.47509 1.54585 7.36218C1.65876 7.24927 1.81191 7.18583 1.9716 7.18583H12.269C12.4196 7.18583 12.5641 7.12598 12.6707 7.01945C12.7772 6.91292 12.8371 6.76843 12.8371 6.61777C12.8371 6.46711 12.7772 6.32262 12.6707 6.21609C12.5641 6.10955 12.4196 6.0497 12.269 6.0497H1.9716C1.51059 6.0497 1.06847 6.23284 0.742487 6.55882C0.416508 6.8848 0.233374 7.32692 0.233374 7.78793V21.3618C0.233374 21.8228 0.416508 22.2649 0.742487 22.5909C1.06847 22.9168 1.51059 23.1 1.9716 23.1H21.1622C21.6232 23.1 22.0653 22.9168 22.3913 22.5909C22.7173 22.2649 22.9004 21.8228 22.9004 21.3618V15.511C22.9004 15.3603 22.8406 15.2158 22.734 15.1093C22.6275 15.0028 22.483 14.9429 22.3323 14.9429Z" fill="#004717" stroke="#004717" stroke-width="0.2"/>
<path d="M25.3058 3.67485L25.3058 3.67474C25.2917 3.54354 25.2325 3.42133 25.1381 3.32908C25.0444 3.23737 24.9217 3.18095 24.7911 3.16936C23.9086 3.055 23.0469 2.81493 22.2323 2.45646L22.2323 2.45644L22.2305 2.45567C21.3893 2.10541 20.6056 1.63065 19.9057 1.04733C19.8012 0.952547 19.6652 0.900015 19.524 0.900015C19.3829 0.900015 19.2469 0.952552 19.1424 1.04734C18.4426 1.63091 17.6589 2.10569 16.8177 2.45566L16.8177 2.45564L16.8158 2.45647C16.0056 2.8134 15.1488 3.05342 14.2711 3.16934C14.1401 3.18039 14.017 3.23651 13.9228 3.32819C13.8279 3.4205 13.7682 3.54306 13.754 3.67469L13.754 3.67485C13.7433 3.77648 13.6206 4.99904 13.6455 6.48637C13.6704 7.96886 13.842 9.73448 14.431 10.9103L14.4311 10.9103C15.1328 12.309 16.3173 13.3963 17.3378 14.1402C18.3451 14.8745 19.2002 15.2797 19.2928 15.3235L19.2963 15.3252L19.2972 15.3256C19.3719 15.3602 19.4534 15.3781 19.5358 15.3781C19.6181 15.3781 19.6996 15.3602 19.7743 15.3256L19.7752 15.3252L19.7787 15.3235C19.8713 15.2796 20.7264 14.8745 21.7337 14.1402C22.7542 13.3963 23.9387 12.309 24.6404 10.9103L24.6406 10.9099C25.2237 9.73421 25.3923 7.96873 25.4157 6.48627C25.4392 4.99899 25.3165 3.77646 25.3058 3.67485ZM23.6121 10.4017L23.6121 10.4018C23.0959 11.433 22.2396 12.2905 21.42 12.9377C20.636 13.557 19.8913 13.9794 19.5241 14.174C19.1569 13.9794 18.4121 13.557 17.6281 12.9377C16.8085 12.2905 15.9522 11.433 15.436 10.4018C15.0156 9.56088 14.8438 8.287 14.79 7.07033C14.7388 5.91103 14.7951 4.81856 14.8369 4.2336C15.6706 4.08231 16.4845 3.83662 17.2628 3.50119C18.0667 3.16119 18.8261 2.72441 19.524 2.2006C20.222 2.72441 20.9814 3.16119 21.7853 3.50119C22.5637 3.83664 23.3776 4.08234 24.2114 4.23363C24.2542 4.81842 24.3111 5.91094 24.2598 7.07033C24.2061 8.28699 24.0337 9.56085 23.6121 10.4017ZM14.8434 20.2916H19.524C19.6747 20.2916 19.8192 20.2318 19.9257 20.1252C20.0323 20.0187 20.0921 19.8742 20.0921 19.7236C20.0921 19.5729 20.0323 19.4284 19.9257 19.3219C19.8192 19.2154 19.6747 19.1555 19.524 19.1555H14.8434C14.6928 19.1555 14.5483 19.2154 14.4417 19.3219C14.3352 19.4284 14.2754 19.5729 14.2754 19.7236C14.2754 19.8742 14.3352 20.0187 14.4417 20.1252C14.5483 20.2318 14.6928 20.2916 14.8434 20.2916ZM14.8434 17.4832H17.6518C17.8025 17.4832 17.9469 17.4234 18.0535 17.3169C18.16 17.2103 18.2199 17.0658 18.2199 16.9152C18.2199 16.7645 18.16 16.62 18.0535 16.5135C17.9469 16.407 17.8025 16.3471 17.6518 16.3471H14.8434C14.6928 16.3471 14.5483 16.407 14.4417 16.5135C14.3352 16.62 14.2754 16.7645 14.2754 16.9152C14.2754 17.0658 14.3352 17.2103 14.4417 17.3169C14.5483 17.4234 14.6928 17.4832 14.8434 17.4832Z" fill="#004717" stroke="#004717" stroke-width="0.2"/>
</svg>

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
src/Images/dash_icon_6n.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,3 @@
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.3266 4.69537e-06C9.04078 -0.00159744 7.78798 0.406843 6.75022 1.16597C5.71246 1.92511 4.9438 2.99538 4.55594 4.22126C4.16807 5.44715 4.18122 6.76477 4.59346 7.98268C5.0057 9.20058 5.79556 10.2553 6.84826 10.9936C5.02658 11.7216 3.46442 12.9781 2.36278 14.6013C1.26114 16.2245 0.670418 18.1403 0.666626 20.102C0.681557 20.3447 0.788501 20.5726 0.965653 20.7392C1.14281 20.9058 1.37683 20.9986 1.62 20.9986C1.86318 20.9986 2.0972 20.9058 2.27435 20.7392C2.45151 20.5726 2.55845 20.3447 2.57338 20.102C2.57019 18.6931 2.94414 17.3091 3.6564 16.0935C4.36867 14.8779 5.3933 13.8751 6.62395 13.1891C7.85459 12.5032 9.24639 12.1591 10.6549 12.1926C12.0634 12.2262 13.4373 12.636 14.6339 13.3797C14.7409 13.446 14.8598 13.4905 14.984 13.5108C15.1082 13.5311 15.2352 13.5267 15.3577 13.498C15.4802 13.4692 15.5958 13.4165 15.698 13.3431C15.8001 13.2696 15.8868 13.1767 15.9531 13.0697C16.0194 12.9628 16.0639 12.8438 16.0842 12.7196C16.1045 12.5954 16.1001 12.4684 16.0713 12.3459C16.0425 12.2234 15.9899 12.1078 15.9164 12.0056C15.843 11.9035 15.7501 11.8168 15.6431 11.7505C15.0892 11.4171 14.5036 11.1394 13.895 10.9215C15.398 9.82213 16.382 8.05235 16.382 6.05548C16.382 2.72857 13.6535 4.69537e-06 10.3266 4.69537e-06ZM10.3266 1.91036C12.6226 1.91036 14.4717 3.75945 14.4717 6.05548C14.4717 8.35152 12.6226 10.197 10.3266 10.197C8.03052 10.197 6.18504 8.35873 6.18504 6.05548C6.18504 3.75945 8.02692 1.91036 10.3266 1.91036ZM19.9757 13.3581C19.8208 13.3625 19.6694 13.4045 19.5343 13.4805C19.3993 13.5565 19.2848 13.6641 19.2007 13.7942L15.9135 18.7215L13.8517 17.341C13.7473 17.264 13.6284 17.2089 13.5021 17.1792C13.3759 17.1494 13.2449 17.1456 13.1171 17.1679C12.9893 17.1903 12.8673 17.2384 12.7587 17.3092C12.65 17.3801 12.5568 17.4722 12.4848 17.5801C12.4128 17.688 12.3634 17.8094 12.3397 17.937C12.3159 18.0645 12.3183 18.1956 12.3467 18.3222C12.3751 18.4487 12.4289 18.5683 12.5048 18.6735C12.5807 18.7787 12.6772 18.8674 12.7884 18.9342L15.6539 20.8409C15.8648 20.9807 16.1225 21.0312 16.3705 20.9812C16.6185 20.9311 16.8365 20.7848 16.9768 20.5742L20.7939 14.8503C20.893 14.7048 20.9499 14.5346 20.9584 14.3587C20.9668 14.1828 20.9265 14.008 20.8418 13.8536C20.7572 13.6992 20.6315 13.5712 20.4786 13.4838C20.3257 13.3963 20.1517 13.3528 19.9757 13.3581Z" fill="#004717"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -7,70 +7,103 @@ import {
Input,
Text,
Tooltip,
useBoolean,
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 { useGetDeleteHistoryQuery } from "../../Services/delete.request.service";
import { TABLE_PAGINATION } from "../../Constants/Paginations";
import InitiateReversalPopup from "../../Components/Popups/InitiateReversalPopups";
import ToastBox from "../../Components/ToastBox";
import { useCreateAccountDeletionReversalRequestMutation } from "../../Services/reversal.account.deletion.service";
import { isMaker } from "../../Constants/Constants";
// import { formatDate } from "../../Components/Functions/UTCConvertor";
const DeletionHistory = () => {
const toast = useToast();
const { slideFromRight, deleteHistory, setDeleteHistory } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const { slideFromRight, setDeleteHistory } = useContext(GlobalStateContext);
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);
// =========================== [Use State] =============================
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const [searchTerm, setSearchTerm] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
const [reversalId, setReversalId] = useState();
const {
isOpen: isOpenInRev,
onOpen: onOpenInRev,
onClose: onCloseInRev,
} = useDisclosure();
const [isReversalLoading, setIsReversalLoading] = useBoolean();
// ====================================================[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);
const [createAccountDeletionReversalRequest] =
useCreateAccountDeletionReversalRequestMutation();
// 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);
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
}
);
return nameMatches;
});
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",
isMaker() && "Reversal Action",
];
const extractedArray = filteredData?.map((item, index) => ({
const extractedArray = deleteHistory?.data?.rows?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Text
@@ -78,83 +111,122 @@ const DeletionHistory = () => {
as={"span"}
color={"gray.800"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
fontWeight={"500"}
>
{index + 1}.
</Text>
),
"Date": (
"Request On": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
fontWeight={"500"}
>
{formatDate(item.date)}
{formatDate(item.Requested_on)}
</Text>
),
"Distribution Amount": (
"Client ID": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
fontWeight={"500"}
>
{item.Distribution}
{item.clientId}
</Text>
),
"Charges (USD)": (
"First Name": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.800"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
fontWeight={"500"}
>
{item.charge}
{item.firstName}
{/* {formatDate(item.charge)} */}
</Text>
),
Year: (
"Last Name": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.800"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
fontWeight={"500"}
>
{item.year}
{item.lastName}
</Text>
),
Quater: (
<Text
Country: (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
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>
"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 === "Approved" ? "red.500" : "blue.500"}
className="d-flex align-items-center web-text-small"
fontWeight={"600"}
>
{item.deletionStatus}
</Text>
),
"Reversal Action": (
<Box w={"120px"} isTruncated={true} cursor={"pointer"}>
{item.deletionStatus === "Approved" ? (
<Text
as={"span"}
color={!item.isReversal ? "green.500" : "#FFBB00"}
fontWeight={700}
>
{!item.isReversal ? (
<Button
onClick={() => {
onOpenInRev(); // Call the function
setReversalId(item.id);
}}
colorScheme="teal"
size="xs"
variant="outline"
>
Initiate Reversal
</Button>
) : (
"Under process"
)}
</Text>
) : (
""
)}
</Box>
),
}));
const handleDelete = () => {
const deleteHistory = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
const deleteHistory = sponser.filter((sponsor) => sponsor.id !== actionId);
setTimeout(() => {
setSponser(deleteHistory);
@@ -164,6 +236,45 @@ const DeletionHistory = () => {
setIsLoading(true);
};
const handleApproved = async (data) => {
setIsReversalLoading.on(); // Start loading
try {
const { error, data: responseData } =
await createAccountDeletionReversalRequest({
id: reversalId,
data,
});
if (error) {
throw error; // Explicitly handle the error
}
// Success: Perform necessary actions
refetch();
toast({
render: () => (
<ToastBox message={responseData?.message || "Action successful!"} />
),
});
onCloseInRev();
} catch (error) {
// Handle errors
toast({
render: () => (
<ToastBox
message={
error?.data?.message || "Something went wrong. Please try again."
}
status="error"
/>
),
});
console.error("Error:", error);
} finally {
setIsReversalLoading.off(); // Ensure loading is toggled off
}
};
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<Box bg="white.500">
@@ -187,13 +298,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}
@@ -213,6 +324,12 @@ const DeletionHistory = () => {
alertHandler={handleDelete}
isLoading={isLoading}
/>
<InitiateReversalPopup
onClose={onCloseInRev}
isOpen={isOpenInRev}
handelApproved={handleApproved}
isLoading={isReversalLoading}
/>
</Box>
);
};

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) => ({
const extractedArray = data?.data?.rows?.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,236 @@
import {
Badge,
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 {
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,
useGetDeleteRequestQuery,
} from "../../Services/delete.request.service";
const FILE_TYPES = ["image/jpeg", "image/png", "image/gif"];
// export const conformModalSchema = yup.object().shape({
// adminComment: yup.string().notRequired(),
// });
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,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
mode: "all",
});
const { refetch } = useGetDeleteRequestQuery();
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} />,
});
refetch();
heandleOnClose();
}
} catch (error) {
console.log(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 comment...."}
resize={"none"}
mb={2}
/>
{errors.adminComment && (
<Text fontSize="xs" color="red">
{errors.adminComment.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Text as={"span"} me={2}>
{" "}
Maximum length should be 200 characters. You have entered
</Text>
{watch("adminComment")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="red"
mr={3}
type="submit"
size={"sm"}
rounded={"sm"}
variant={"ghost"}
onClick={() => setIsReject(true)}
isLoading={isBtnLoadingReject}
fontWeight={500}
>
Reject
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Approve
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default DeletionRequestApprove;

View File

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

View File

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

View File

@@ -5,14 +5,6 @@ import {
Button,
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
@@ -116,7 +105,7 @@ const BankDetails = () => {
});
const extractedArray = filteredData?.map((item) => ({
const extractedArray = filteredData?.map((item,index) => ({
id: item?.id,
"Sr N/O": (
<Text
@@ -131,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}
@@ -157,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}`);
}}
@@ -227,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

@@ -20,14 +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, useUpdateContactMutation } 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) {
@@ -39,10 +60,10 @@ export function debounce(func, delay) {
}
const Contact = () => {
const toast = useToast()
const toast = useToast();
const navigate = useNavigate();
const [form, setForm] = useState({});
const [ isLoading, setIsLoading ] = useState(false)
const [isLoading, setIsLoading] = useState(false);
// const { sponser, setSponser } = useContext(GlobalStateContext);
const {
@@ -52,16 +73,17 @@ const Contact = () => {
formState: { errors },
} = useForm({
resolver: yupResolver(addSponser),
// mode: all
});
console.log(errors);
const {
data: contact,
isLoading: contactLoading,
error,
} = useGetContactQuery();
const [ updateContact ] = useUpdateContactMutation()
const [updateContact] = useUpdateContactMutation();
useEffect(() => {
if (contact) {
@@ -84,7 +106,7 @@ const Contact = () => {
name: "phoneNumber",
type: "text",
isRequired: true,
section: "Add Details",
section:"",
// value: contact?.phoneNumber || "",
},
{
@@ -93,7 +115,7 @@ const Contact = () => {
placeHolder: " ",
type: "text",
isRequired: true,
section: "Add Details",
section:"",
// value: contact?.emailAddress || "",
},
{
@@ -102,7 +124,7 @@ const Contact = () => {
placeHolder: " ",
type: "text",
isRequired: true,
section: "Add Details",
section:"",
// value: contact?.websiteUrl || "",
},
];
@@ -117,24 +139,20 @@ const Contact = () => {
}, {});
const onSubmit = async (data) => {
setIsLoading(true)
setIsLoading(true);
try {
const res = await updateContact(data)
const res = await updateContact(data);
if (res?.data?.statusCode === 200) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
setIsLoading(false)
setIsLoading(false);
}
} catch (error) {
console.log(error);
setIsLoading(false)
setIsLoading(false);
}
};
return (

View File

@@ -0,0 +1,364 @@
import {
Badge,
Box,
HStack,
Input,
Select,
Text,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import ToastBox from "../../../../Components/ToastBox";
import { TABLE_PAGINATION } from "../../../../Constants/Paginations";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import { useGetbanInvestorQuery } from "../../../../Services/ban.investor.service";
import { debounce } from "../../../Master/Sponser/AddSponser";
import ReasonBanModal from "./ReasonBanModal";
import Pagination from "../../../../Components/Pagination";
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>
),
}));
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"}>
<Pagination
isLoading={unbanLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={data?.data?.totalItems}
/>
{/* <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,355 @@
import {
Badge,
Box,
HStack,
Input,
Select,
Text,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import DataTable from "../../../../Components/DataTable/NormalTable";
import ToastBox from "../../../../Components/ToastBox";
import { generateSerialNumber } from "../../../../Constants/Constants";
import { TABLE_PAGINATION } from "../../../../Constants/Paginations";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import { useGetUnbanInvestorQuery } from "../../../../Services/ban.investor.service";
import { debounce } from "../../../Master/Sponser/AddSponser";
import ReasonBanModal from "./ReasonBanModal";
import Pagination from "../../../../Components/Pagination";
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"}>
<Pagination
isLoading={unbanLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={data?.data?.totalItems}
/>
{/* <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,404 @@
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,
HStack,
Input,
Select,
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 {
INVESTOR_TABLE_PAGINATION,
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("Notification Header 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("Notification Header is required"),
message: yup.string().notRequired(),
});
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(INVESTOR_TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(
INVESTOR_TABLE_PAGINATION?.page
);
const [searchTerm, setSearchTerm] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
const [country, setCountry] = useState("");
const [kyc, setKyc] = 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 });
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearchTerm(searchTerm.trim()); // Trim to remove leading/trailing spaces
}, 300);
return () => clearTimeout(handler);
}, [searchTerm]);
const {
data: investorDetails,
isLoading: investorDetailsLoading,
refetch,
} = useGetUnbanInvestorQuery(
{
page: 1, // Omit pagination for search
size: 10000, // Omit pagination for search
// page: debouncedSearchTerm ? undefined : currentPage, // Disable pagination for search
// size: debouncedSearchTerm ? undefined : 10000 || pageSize || 500, // Disable pagination for search
search: debouncedSearchTerm, // Pass search term
country_xid: country,
KYCStatus: kyc,
},
{
skip: searchTerm !== "" && debouncedSearchTerm === "", // Skip if search not debounced yet
}
);
// useEffect(() => {
// console.log("Search Term:", searchTerm);
// console.log("Debounced Search Term:", debouncedSearchTerm);
// console.log("Investor Details:", investorDetails);
// }, [searchTerm, debouncedSearchTerm, 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);
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" : "Not Completed"}
</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}
>
<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 className="col" justifyContent={"end"}>
<Select
w={250}
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">Bahrain</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>
<Select
w={250}
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">Not Completed</option>
<option value="1">Completed</option>
</Select>
</HStack>
</HStack>
<Box overflow={"scroll"} h={"58vh"}>
<NormalTable
centered={true}
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={investorDetailsLoading}
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;

View File

@@ -0,0 +1,448 @@
import {
Badge,
Box,
Button,
HStack,
Input,
Switch,
Text,
Tooltip,
useToast,
useDisclosure,
Link,
useBoolean,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { CheckIcon, CloseIcon, ExternalLinkIcon } from "@chakra-ui/icons";
import { debounce } from "../Admin/Contact";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import GlobalStateContext from "../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../Components/CustomAlertDialog";
import ToastBox from "../../Components/ToastBox";
import NormalTable from "../../Components/DataTable/NormalTable";
import { TABLE_PAGINATION } from "../../Constants/Paginations";
import { generateSerialNumber } from "../../Constants/Constants";
import { useGetDepositHistoryQuery } from "../../Services/deposit.request.service";
import Pagination from "../../Components/Pagination";
import ConfirmModal from "./ConfirmModal";
import RejectModal from "./RejectModal";
import {
useApproveBankDepositRequestMutation,
useGetBankDepositMasterQuery,
useRejectbankDepositRequestMutation,
} from "../../Services/bankdeposit.request.service";
import RejectReversalPopups from "../../Components/Popups/RejectReversalPopups";
import ConfirmReversalPopups from "../../Components/Popups/ConfirmReversalPopups";
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
}; // Simple date formatter
const BankDepositRequest = () => {
const navigate = useNavigate();
const toast = useToast();
const { depositHistory, setDepositHistory, 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: 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("");
const [rejectbankDepositRequest] = useRejectbankDepositRequestMutation();
const [approveBankDepositRequest] = useApproveBankDepositRequestMutation();
const [isReversalLoading, setIsReversalLoading] = useBoolean();
// 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,
error,
refetch,
isLoading: depositHistoryLoading,
} = useGetBankDepositMasterQuery(
{
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",
"Request Date",
"Client ID",
"First Name",
"Last Name",
"Country",
"Phone Number",
"Action",
];
const handleUpdateStatus = debounce((id) => {
setDepositHistory((prevDepositHistory) =>
prevDepositHistory.map((depositHistory) =>
depositHistory.id === id
? { ...depositHistory, status: !depositHistory.status }
: depositHistory
)
);
toast({
render: () => <ToastBox message={"Status changed succesfully.!"} />,
});
}, 300);
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 (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 = 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>
),
"Request 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?.isReversalDate)}
</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>
</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>
),
Action: (
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="Approve"
bg="#fff"
color={"green.500"}
placement="left-start"
>
<Button
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 IOtype = investmentType.filter(
(investmentType) => investmentType.id !== actionId
);
setTimeout(() => {
setInvestmentType(IOtype);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const handleApproved = async (data) => {
setIsReversalLoading.on(); // Start loading
try {
const { error, data: responseData } = await rejectbankDepositRequest({
id: actionId,
data,
});
if (error) {
throw error; // Explicitly handle the error
}
// Success: Perform necessary actions
refetch();
toast({
render: () => (
<ToastBox message={responseData?.message || "Action successful!"} />
),
});
onRejectClose();
} catch (error) {
// Handle errors
toast({
render: () => (
<ToastBox
message={
error?.data?.message || "Something went wrong. Please try again."
}
status="error"
/>
),
});
console.error("Error:", error);
} finally {
setIsReversalLoading.off(); // Ensure loading is toggled off
}
};
const handleConfirm = async (data) => {
setIsReversalLoading.on(); // Start loading
try {
const { error, data: responseData } = await approveBankDepositRequest({
id: actionId,
data,
});
if (error) {
throw error; // Explicitly handle the error
}
// Success: Perform necessary actions
refetch();
toast({
render: () => (
<ToastBox message={responseData?.message || "Action successful!"} />
),
});
onRejectClose();
} catch (error) {
// Handle errors
toast({
render: () => (
<ToastBox
message={
error?.data?.message || "Something went wrong. Please try again."
}
status="error"
/>
),
});
console.error("Error:", error);
} finally {
setIsReversalLoading.off(); // Ensure loading is toggled off
}
};
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
{/* <ConfirmModal isOpen={isConfirmOpen} onClose={onConfirmClose} /> */}
<RejectReversalPopups
isOpen={isRejectOpen}
onClose={onRejectClose}
handelApproved={handleApproved}
isLoading={isReversalLoading}
/>
<ConfirmReversalPopups
isOpen={isConfirmOpen}
onClose={onConfirmClose}
handleConfirm={handleConfirm}
isLoading={isReversalLoading}
/>
<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
isLoading={depositHistoryLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={data?.data?.totalItems}
/>
</HStack>
</HStack>
</Box>
<NormalTable
emptyMessage={`We don't have any Investment type `}
tableHeadRow={tableHeadRow}
// setData={setExtractedArray}
data={extractedArray}
isLoading={depositHistoryLoading}
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 BankDepositRequest;

View File

@@ -0,0 +1,149 @@
import {
Box,
Button,
Checkbox,
FormControl,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
} from "@chakra-ui/react";
import React, { useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ReactQuill from "react-quill";
export const conformModalSchema = yup.object().shape({
fees: yup.string().required("File name is required"),
totalAmount: yup.string().required("File name is required"),
});
const ConfirmModal = ({ isOpen, onClose, firstField }) => {
const [emailApproval,setEmailApproval] = useState(false)
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);
};
const modules = {
toolbar: [
// [{ header: "1" }, { header: "2" },
// // { font: [] }
// ],
// [{ size: [] }],
["bold", "italic", "underline", "strike", "blockquote"],
[{ list: "ordered" }, { list: "bullet" }],
["clean"],
],
};
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} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea rows={5}
focusBorderColor='green.400'
name="fileName"
{...register("fileName")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comments...."}
rounded={'md'}
resize={'none'}
/>
{errors.comment && (
<Text fontSize="xs" color="red">
{errors.comment.message}
</Text>
)}
</FormControl>
<Checkbox colorScheme='forestGreen'
onChange={(e) =>setEmailApproval(e.target.checked)}
>
<Text mb={0} fontSize={'sm'}>Send an email to the user upon approval</Text>
</Checkbox>
{emailApproval && <Box className="messageBox">
<FormControl mb={4}>
<FormLabel fontSize="sm" mb={1}>Subject</FormLabel>
<Input
focusBorderColor='green.400'
name="fileName"
{...register("fileName")}
fontSize="sm"
type="text"
size="sm"
/>
</FormControl>
<FormControl mb={12}>
<FormLabel fontSize="sm" mb={1}>Message</FormLabel>
<ReactQuill
theme="snow"
style={{
height: 150,
}}
// value={value}
// onChange={setValue}
modules={modules}
placeholder="Start typing here..."
/>
</FormControl>
</Box>}
</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 ConfirmModal;

View File

@@ -0,0 +1,139 @@
import {
Box,
Button,
FormControl,
FormLabel,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useToast,
} from "@chakra-ui/react";
import React, { useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import { useRejectbankDepositRequestMutation } from "../../Services/bankdeposit.request.service";
import ToastBox from "../../Components/ToastBox";
export const conformModalSchema = yup.object().shape({
comments: yup.string().required("Comment is required"),
});
const RejectModal = ({ isOpen, onClose, firstField ,id}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const [ rejectbankDepositRequest ] = useRejectbankDepositRequestMutation()
const onSubmit = async(data) => {
console.log(id);
setIsBtnLoading(true)
try {
const res = await rejectbankDepositRequest({ 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?.success} />
),
});
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"}>Reject</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}
>
Send
</Button>
</ModalFooter>
</Box>
</ModalContent>
</Modal>
);
};
export default RejectModal;

View File

@@ -0,0 +1,245 @@
import {
Button,
DrawerFooter,
FormControl,
FormErrorMessage,
FormLabel,
Input,
InputGroup,
InputRightElement,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalOverlay,
Stack,
useToast,
} from "@chakra-ui/react";
import { yupResolver } from "@hookform/resolvers/yup";
import React, { useState } from "react";
import { useForm } from "react-hook-form";
import * as yup from "yup";
import CustomAlertDialog from "../Components/CustomAlertDialog";
import ToastBox from "../Components/ToastBox";
import { useUpdatePasswordMutation } from "../Services/change.password.service";
// Validation schema
const passwordSchema = yup.object().shape({
oldPassword: yup.string().required("Current Password is required"),
newPassword: yup
.string()
.required("New Password is required")
.min(8, "Password must be at least 8 characters long")
.max(16, "Password must be at most 50 characters long")
.matches(/[A-Z]/, "Password must contain at least one uppercase letter")
.matches(/[a-z]/, "Password must contain at least one lowercase letter")
.matches(/[0-9]/, "Password must contain at least one number")
.matches(
/[@$!%*?&#]/,
"Password must contain at least one special character"
),
confirmNewPassword: yup
.string()
.required("Confirm New Password is required")
.oneOf([yup.ref("newPassword")], "Password do not match"),
});
const ChangePassword = ({
isOpen,
onClose,
firstField,
actionId,
setActionId,
}) => {
const initialValue = {
oldPassword: "",
newPassword: "",
confirmNewPassword: "",
};
const [isLoading, setIsLoading] = useState(false);
const [alert, setAlert] = useState(false);
const [showCurrentPassword, setShowCurrentPassword] = useState(false);
const [showNewPassword, setShowNewPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const toast = useToast();
const [input, setInput] = useState(initialValue);
const [updatePassword] = useUpdatePasswordMutation();
// Form handling
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm({
resolver: yupResolver(passwordSchema),
mode: "all",
});
// Form submit handler
const onSubmit = async () => {
setIsLoading(true);
try {
const res = await updatePassword(input); // Assuming API request works as expected
if (res?.data?.statusCode === 200) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
handleClose();
} else if (res?.error) {
toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} />
),
});
setAlert(false);
}
} catch (error) {
console.error(error);
} finally {
setIsLoading(false);
}
};
const handleSubmitFrom = (data) => {
setAlert(true);
setInput(data);
};
// Handle modal close
const handleClose = () => {
setAlert(false);
onClose();
reset();
};
return (
<>
<Modal isOpen={isOpen} onClose={handleClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent>
<ModalHeader fontSize="md">Change Password</ModalHeader>
<ModalCloseButton />
<ModalBody pb={6}>
<Stack spacing={4}>
{/* Current Password */}
<FormControl isInvalid={errors.oldPassword} isRequired>
<FormLabel fontSize="sm" mb={1} fontWeight={500}>
Current Password
</FormLabel>
<InputGroup size="sm">
<Input
{...register("oldPassword")}
fontSize="sm"
type={showCurrentPassword ? "text" : "password"}
focusBorderColor="forestGreen.300"
/>
<InputRightElement width="4.5rem">
<Button
h="1.5rem"
size="xs"
onClick={() => setShowCurrentPassword((prev) => !prev)}
>
{showCurrentPassword ? "Hide" : "Show"}
</Button>
</InputRightElement>
</InputGroup>
<FormErrorMessage>
{errors.oldPassword?.message}
</FormErrorMessage>
</FormControl>
{/* New Password */}
<FormControl isInvalid={errors.newPassword} isRequired>
<FormLabel fontSize="sm" mb={1}>
New Password
</FormLabel>
<InputGroup size="sm">
<Input
{...register("newPassword")}
fontSize="sm"
type={showNewPassword ? "text" : "password"}
focusBorderColor="forestGreen.300"
/>
<InputRightElement width="4.5rem">
<Button
h="1.5rem"
size="xs"
onClick={() => setShowNewPassword((prev) => !prev)}
>
{showNewPassword ? "Hide" : "Show"}
</Button>
</InputRightElement>
</InputGroup>
<FormErrorMessage>
{errors.newPassword?.message}
</FormErrorMessage>
</FormControl>
{/* Confirm Password */}
<FormControl isInvalid={errors.confirmNewPassword} isRequired>
<FormLabel fontSize="sm" mb={1}>
Confirm New Password
</FormLabel>
<InputGroup size="sm">
<Input
{...register("confirmNewPassword")}
fontSize="sm"
type={showConfirmPassword ? "text" : "password"}
focusBorderColor="forestGreen.300"
/>
<InputRightElement width="4.5rem">
<Button
h="1.5rem"
size="xs"
onClick={() => setShowConfirmPassword((prev) => !prev)}
>
{showConfirmPassword ? "Hide" : "Show"}
</Button>
</InputRightElement>
</InputGroup>
<FormErrorMessage>
{errors.confirmNewPassword?.message}
</FormErrorMessage>
</FormControl>
</Stack>
</ModalBody>
<DrawerFooter mb={5}>
<Button
onClick={handleClose}
bg="#e0ebeb"
size="sm"
mr={3}
rounded={"sm"}
>
Cancel
</Button>
<Button
rounded={"sm"}
colorScheme="forestGreen"
size="sm"
// onClick={() => setAlert(true)}
onClick={handleSubmit(handleSubmitFrom)}
isLoading={isLoading}
>
Save
</Button>
</DrawerFooter>
</ModalContent>
</Modal>
<CustomAlertDialog
isOpen={alert}
onClose={() => setAlert(false)}
alertHandler={onSubmit}
message={"Are you sure you want to change the password?"}
isLoading={isLoading}
/>
</>
);
};
export default ChangePassword;

View File

@@ -135,9 +135,6 @@ const Dashbaord = () => {
</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'} >
@@ -151,9 +148,6 @@ const Dashbaord = () => {
</VStack>
</Box>
</Box>
</Box>
</Box>
)

View File

@@ -0,0 +1,504 @@
import {
Box,
Divider,
Flex,
Heading,
HStack,
Image,
Select,
SimpleGrid,
Text,
} from "@chakra-ui/react";
import React, { useState } from "react";
import {
BsArrowsAngleContract,
BsBoxArrowDown,
BsBoxArrowUp,
} from "react-icons/bs";
import { FaRegPenToSquare } from "react-icons/fa6";
import { LuContact } from "react-icons/lu";
import { Link } from "react-router-dom";
import { useGetDashboardMasterQuery } from "../../Services/dashboard.service";
import InvestmentOpportunities from "./InvestmentOpportunities";
import IconOne from '../../Images/dash_icon_1.svg';
import IconTwo from '../../Images/dash_icon_2.svg';
import IconThree from '../../Images/dash_icon_3n.png';
import IconFour from '../../Images/dash_icon_4.svg'
import IconFive from '../../Images/dash_icon_5.svg';
import IconSix from '../../Images/dash_icon_6n.png';
import IconSeven from '../../Images/dash_icon_7.svg'
// const panddingReguest = [
// {
// icon: <BsBoxArrowUp />,
// label: "Pending deposit requests",
// count: 27,
// },
// {
// icon: <BsBoxArrowDown />,
// label: "Pending withdrawal requests",
// count: 27,
// },
// {
// icon: <LuContact />,
// label: "Pending KYC review request",
// count: 27,
// },
// {
// icon: <FaRegPenToSquare />,
// label: "Pending Acc modification request",
// count: 27,
// },
// ];
const Dashbaord = ({ showSearch = false }) => {
const [filter, setFilter] = useState("today");
const { data, error, isLoading } = useGetDashboardMasterQuery();
console.log(data);
const panddingReguest = [
{
icon: IconOne,
label: "Pending deposit requests",
count: parseFloat(
data?.data?.pendingRequests?.pendingDepositReq?.totalDepositReq || 0
).toLocaleString(),
},
{
icon: IconTwo,
label: "Pending withdrawal requests",
count: parseFloat(
data?.data?.pendingRequests?.pendingWithdrawalReq || 0
).toLocaleString(),
},
{
icon: IconThree,
label: "Pending KYC review request",
count: parseFloat(
data?.data?.pendingRequests?.pendingKYCReviewReq || 0
).toLocaleString(),
},
{
icon: IconFour,
label: "Pending Acc modification request",
count: parseFloat(
data?.data?.pendingRequests?.pendingAccModReq?.totalAccModReq || 0
).toLocaleString(),
},
];
const userActivity = [
{
icon: IconFive,
label: "Total number of users",
count: data?.data?.userActivity?.totalCount, //totalCount
},
{
icon: IconSix,
label: "Total number of users with completed KYC (%)",
count: data?.data?.userActivity?.KYCCompletedCount, //KYCCompletedCount
percentage: data?.data?.userActivity?.completedKYCPer, //completedKYCPer
},
{
icon: IconSeven,
label: "Total number of Active users",
count: data?.data?.userActivity?.ActiveUsers, //ActiveUsers
},
];
const closedOpportunities = [
{
icon: IconOne,
label: "Closed Opportunities",
count: parseFloat(
data?.data?.investmentOpportunity?.closedOpportunityDetails
?.numberOfClosedOpportunities || 0
).toLocaleString(),
},
{
icon: IconOne,
label: "Total assets under management",
count: `$ ${parseFloat(
data?.data?.investmentOpportunity?.closedOpportunityDetails?.totalAUM ||
0
).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`,
},
{
icon: IconOne,
label: "Total number of investors",
count: parseFloat(
data?.data?.investmentOpportunity?.closedOpportunityDetails
?.totalNumberOfInvestors || 0
).toLocaleString(),
},
{
icon: IconOne,
label: "Total number of unique investors",
count: parseFloat(
data?.data?.investmentOpportunity?.closedOpportunityDetails
?.numberOfUniqueInvestors || 0
).toLocaleString(),
},
];
const walletCase = [
{
amount: `$ ${parseFloat(
data?.data?.walletBalance?.usersWalletCashInUSD || 0
).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`,
currency: "USD",
},
{
amount: ` د. ب. ${parseFloat(
data?.data?.walletBalance?.usersWalletCashInBHD || 0
).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`,
currency: "BHD",
},
{
amount: ` ﷼. ${parseFloat(
data?.data?.walletBalance?.usersWalletCashInSAR || 0
).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`,
currency: "SAR",
},
{
amount: ` د.ك ${parseFloat(
data?.data?.walletBalance?.usersWalletCashInKWD || 0
).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`,
currency: "KWD",
},
{
amount: ` د.إ ${parseFloat(
data?.data?.walletBalance?.usersWalletCashInAED || 0
).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`,
currency: "AED",
},
{
amount: ` ﷼. ${parseFloat(
data?.data?.walletBalance?.usersWalletCashInOMR || 0
).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`,
currency: "OMR",
},
{
amount: ` ر. ق. ${parseFloat(
data?.data?.walletBalance?.usersWalletCashInQAR || 0
).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`,
currency: "QAR",
},
];
return (
<Box
height={"100vh"}
bg={"#fff"}
roundedTop={0}
pt={5}
pr={4}
overflowX={"hidden"}
mb={5}
>
<Box display={"flex"} gap={5} mb={5}>
<Box w={"40%"} bg={"#0047170F"} p={4} rounded={5}>
<Heading fontSize={"md"} fontWeight={500}>
Pending Request
</Heading>
<Divider borderBottom={'1px solid #00471785'} />
<SimpleGrid columns={2} spacing={3}>
{panddingReguest.map((item, index) => (
<Box key={index} bg="white" p={3} pb={2} rounded={10} border={'1px solid #00471785'}>
<Text fontSize={"18px"} mb={2}>
<Image w={"20px"} src={item.icon} />
</Text>
<Text mb={0} mt={0} fontSize={"11px"} fontWeight={500}>
{item.label}
</Text>
<Text as="span" fontSize={"md"} fontWeight={700} mb={0}>
{item.count}
</Text>
</Box>
))}
</SimpleGrid>
</Box>
<Box bg={"#0047170F"} w={"65%"} p={4} rounded={5}>
<Heading fontSize={"md"} fontWeight={500}>
User Activity
</Heading>
<Divider borderBottom={'1px solid #00471785'} />
<SimpleGrid columns={3} spacing={3}>
{userActivity.map((item, index) => (
<Box key={index} bg="white" p={3} pb={2} rounded={10} border={'1px solid #00471785'}>
<Flex alignContent={"center"}>
<Text fontSize={"18px"} mb={0}>
{item.icon === IconSix ? (
<Image w={"35px"} src={item.icon} />
) : item.icon === IconSeven ? (
<Image w={"18px"} src={item.icon} />
) : (
<Image w={"25px"} src={item.icon} />
)}
</Text>
<Text
fontSize={"11px"}
fontWeight={500}
mb={0}
minH={"38px"}
ml={1}
mt={0}
>
{item.label}
</Text>
</Flex>
<Text as="span" fontSize={"md"} fontWeight={700} mb={0}>
{item?.count}
{item?.percentage && (
<Text
as={"span"}
fontSize={"12px"}
color={"#666"}
fontWeight={400}
>
{` (${item?.percentage}) %`}
</Text>
)}
</Text>
</Box>
))}
</SimpleGrid>
<Box display={"flex"} gap={3} mt={4}>
<Box w={"50%"} bg={"#fff"} px={4} rounded={10} border={'1px solid #0047171f'}>
<Box h={"25px"}></Box>
<HStack justifyContent={"space-between"}>
<Box textAlign={"center"}>
<Text
as={"span"}
color={"#066123"}
fontSize={"md"}
fontWeight={500}
>
{data?.data?.userActivity?.openedAppToday?.percentage} %
</Text>
<Text fontSize={"11px"} fontWeight={500} mb={0}>
Users that opened the app today
</Text>
</Box>
<Divider orientation="vertical" h={'70px'} borderLeft={'1px solid #00471785'} />
<Box textAlign={"center"}>
<Text
as={"span"}
color={"#066123"}
fontSize={"md"}
fontWeight={500}
>
{parseFloat(
data?.data?.userActivity?.loggedInWithInLastMonth
?.number || 0
).toLocaleString()}
</Text>
<Text fontSize={"11px"} fontWeight={500} mb={0}>
Users who logged in during the last month
</Text>
</Box>
</HStack>
</Box>
<Box
w={"50%"}
bg={"#fff"}
px={4}
rounded={10}
alignItems={"inherit"}
border={'1px solid #0047171f'}
>
<Box display={"flex"} justifyContent={"end"} position={'relative'} top={3}>
<Select
bg={"#0047170F"}
w={"108px"}
display={"flex"}
alignItems={"center"}
// placeholder="Select"
size="xs"
fontSize={'10px'}
fontWeight={600}
rounded={5}
onChange={(e) => setFilter(e.target.value)}
>
<option value="today" selected>
Today
</option>
<option value="last7days">Last 7 day's</option>
<option value="last30days">Last 30 day's</option>
</Select>
</Box>
<HStack justifyContent={"space-between"}>
<Box textAlign={"center"}>
<Text
as={"span"}
color={"#066123"}
fontSize={"md"}
fontWeight={500}
>
{filter === "today"
? data?.data?.userActivity?.newSignUps?.today
: filter === "last7days"
? data?.data?.userActivity?.newSignUps?.last7days
: data?.data?.userActivity?.newSignUps?.last30days}
</Text>
<Text fontSize={"11px"} fontWeight={500} mb={0}>
New sign-ups{" "}
</Text>
</Box>
<Divider orientation="vertical" h={'70px'} borderLeft={'1px solid #00471785'} />
<Box textAlign={"center"}>
<Text
as={"span"}
color={"#066123"}
fontSize={"md"}
fontWeight={500}
>
{filter === "today"
? data?.data?.userActivity?.newKYCVerifications?.today
: filter === "last7days"
? data?.data?.userActivity?.newKYCVerifications?.last7days
: data?.data?.userActivity?.newKYCVerifications
?.last30days}
</Text>
<Text fontSize={"11px"} fontWeight={500} mb={0}>
New KYC verification
</Text>
</Box>
</HStack>
</Box>
</Box>
</Box>
</Box>
<Box display={"flex"} gap={5}>
<Box w={"50%"} bg={"#0047170F"} p={4} rounded={5}>
<Heading fontSize={"md"} fontWeight={500}>
Investment Opportunities
</Heading>
<Divider borderBottom={'1px solid #00471785'} />
<Box mb={5} bg={"#fff"} py={2} rounded={4} border={'1px solid #0047171f'}>
<HStack
justifyContent={"space-between"}
px={3}
alignItems={"center"}
mb={1}
>
<Heading fontSize={"sm"} fontWeight={500} py={1} mb={0}>
Open Opportunities
</Heading>
<Link to={"/investment-opportunities"}>
<BsArrowsAngleContract fontSize={"18px"} />
</Link>
</HStack>
<Box h={"150px"} overflowY={"scroll"} position={"relative"}>
<InvestmentOpportunities
showSearch={false}
selectStyle={{
right: "5px",
marginBottom: "5px",
bg:'#e6f3e9'
}}
/>
</Box>
</Box>
<SimpleGrid columns={2} spacing={4}>
{closedOpportunities.map((item, index) => (
<Box key={index} bg="white" p={4} rounded={10} border={'1px solid #00471785'}>
<HStack alignItems={"center"}>
<Text fontSize={"17px"} mb={0}>
<Image w={'20px'} src={item.icon} />
</Text>
<Text mb={0} mt={0} fontSize={"11px"} fontWeight={500}>
{item.label}
</Text>
</HStack>
<Text as="span" fontSize={"md"} fontWeight={700} mb={0}>
{/* {parseFloat(item?.count || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})} */}
{item.count}
</Text>
</Box>
))}
</SimpleGrid>
</Box>
<Box w={"50%"} bg={"#0047170F"} p={4} rounded={5}>
<Heading fontSize={"md"} fontWeight={500}>
Wallet Balances
</Heading>
<Divider borderBottom={'1px solid #00471785'} />
<Box bg={"#fff"} p={4} rounded={4} mb={5} border={'1px solid #0047171f'}>
<Text fontSize={"xxl"} fontWeight={600} mb={2}>
${" "}
{parseFloat(
data?.data?.walletBalance?.totalCashAvailAcrossAllUSD || 0
).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
{/* {data?.data?.walletBalance?.totalCashAvailAcrossAllUSD} */}
</Text>
<Text fontSize={"md"} mb={0}>
TOTAL CASH AVAILABLE ACROSS ALL WALLETS USD
</Text>
</Box>
<Text fontSize={"sm"} mb={2}>
Sum of users available wallet cash in
</Text>
<SimpleGrid columns={2} spacing={2} mb={2}>
{walletCase.slice(0, 4).map((item, index) => (
<Box key={index} bg={"#fff"} p={3} rounded={4} border={'1px solid #00471785'}>
<Text mb={1} fontSize={"md"} fontWeight={500}>
{item.amount}
</Text>
<Text mb={0} fontSize={"md"} color={"#00000075"}>
{item.currency}
</Text>
</Box>
))}
</SimpleGrid>
<SimpleGrid columns={3} spacing={2}>
{walletCase.slice(4).map((item, index) => (
<Box key={index} bg={"#fff"} p={3} rounded={4} border={'1px solid #00471785'}>
<Text mb={1} fontSize={"md"} fontWeight={500}>
{item.amount}
</Text>
<Text mb={0} fontSize={"md"} color={"#00000075"}>
{item.currency}
</Text>
</Box>
))}
</SimpleGrid>
</Box>
</Box>
</Box>
);
};
export default Dashbaord;

View File

@@ -0,0 +1,169 @@
import { Badge, Box, HStack, Input, Select, Text, useToast } from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import GlobalStateContext from "../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../Components/CustomAlertDialog";
import NormalTable from "../../Components/DataTable/NormalTable";
import { useGetDashboardMasterQuery } from "../../Services/dashboard.service";
import { generateSerialNumber } from "../../Constants/Constants";
const formatDate = (date) => new Date(date).toLocaleDateString();
const InvestmentOpportunities = ({ showSearch = true, selectStyle = {} }) => {
const toast = useToast();
const { opportunities, setOpportunities, slideFromRight } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(null);
const [filter, setFilter] = useState(null);
const { data, error } = useGetDashboardMasterQuery();
useEffect(() => {
const timer = setTimeout(() => setIsLoading(false), 1500);
return () => clearTimeout(timer);
}, []);
const tableHeadRow = [
"Sr No",
"Investors",
"Amount Remaining %",
"Total Deal Size",
"No of Views",
];
const filteredData =
data?.data?.investmentOpportunity?.openOpportunityDetails?.filter((item) =>
item?.Investment_name?.toLowerCase().includes(searchTerm.toLowerCase())
);
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id,
"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"
>
{index + 1}
</Text>
),
Investors: (
<Box w={"auto"} isTruncated>
<Text as={"span"} color={"teal.900"}>
{item?.Investment_name}
</Text>
</Box>
),
"Amount Remaining %": (
<Box w={"90px"} textAlign={'end'} isTruncated>
<Text as={"span"} color={"teal.900"}>
{parseFloat(item?.Amount_remaining_per || 0).toLocaleString(
undefined,
{
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}
)}<Badge ms={1} colorScheme="green">%</Badge>
</Text>
</Box>
),
"Total Deal Size": (
<Box w={"auto"} isTruncated>
<Text as={"span"} color={"teal.900"}>
<Badge me={1} ms={1} colorScheme="green">$</Badge>{parseFloat(item?.Deal_size || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</Box>
),
"No of Views": (
<Box w={"auto"} isTruncated>
<Text as={"span"} color={"teal.900"}>
{filter === "last30days"
? item?.Views_mtd
: filter === "last7days"
? item?.Views_last_7_days
: item?.Views_today}
</Text>
</Box>
),
}));
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"}>
<Box bg="white.500" display="flex" justifyContent="space-between">
{showSearch && (
<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 display="flex" alignItems="center" justifyContent="end" w="100%">
<Select
{...selectStyle}
// placeholder="Select"
size="xs"
rounded={2}
display="flex"
alignItems="center"
w={120}
h="25px"
_focus={{
outline: "none",
border: "1px solid #ccc",
boxShadow: "none",
}}
onChange={(e) => setFilter(e.target.value)}
>
<option value="today" selected>
Today
</option>
<option value="last7days">Last 7 day&rsquo;s</option>
<option value="last30days">Last 30 day&rsquo;s</option>
</Select>
</Box>
</Box>
<NormalTable
emptyMessage={"We don't have any details"}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete this opportunity?"}
isLoading={isLoading}
/>
</Box>
);
};
export default InvestmentOpportunities;

View File

@@ -35,17 +35,18 @@ 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 { removeTrailingZeros } from "../../../Constants/Constants";
import { formatCurrency } from "../../../Components/CurrencyInput";
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("");
@@ -61,27 +62,62 @@ 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(() => {
const handler = setTimeout(() => {
setDebouncedSearchTerm(searchTerm);
}, 500); // Adjust delay as needed
return () => {
clearTimeout(handler);
};
}, [searchTerm]);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
const {
data,
isLoading: depositRequestLoading,
error,
} = useGetDepositRequestQuery({ page: currentPage, size: pageSize });
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",
"Sr.no",
"Client ID",
"First Name",
"Last Name",
"Country",
"Phone Number",
"Amount in Investor currency",
"Deposit Amount",
"Deposit Date",
"Action",
];
@@ -100,40 +136,39 @@ const DepositRequest = () => {
}, 300);
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((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 (Uncomment and use if needed)
// 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;
})
.sort((b, a) => new Date(a.createdAt) - new Date(b.createdAt));
return nameMatches;
})
.sort((b, a) => new Date(a.createdAt) - new Date(b.createdAt));
console.log(data?.data?.rows);
const extractedArray = filteredData?.map((item, index) => ({
const extractedArray = data?.data?.rows?.map((item, idx) => ({
// id: item?.id,
"Sr.no": (
<Text
w={"30px"}
w={"20px"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{index + 1}
{generateSerialNumber(idx, currentPage, pageSize)}
</Text>
),
"Client ID": (
@@ -149,7 +184,7 @@ const DepositRequest = () => {
</Text>
),
"First Name": (
<Box isTruncated={true} w={"70px"}>
<Box isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.firstName}
</Text>
@@ -176,10 +211,22 @@ const DepositRequest = () => {
</Text>
</Box>
),
"Amount in Investor currency": (
<Box display={'flex'} justifyContent={'end'} w={"100px"} isTruncated={true}>
"Deposit Amount": (
<Box
display={"flex"}
justifyContent={"end"}
w={"130px"}
isTruncated={true}
>
<Text as={"span"} color={"teal.900"}>
{formatCurrency(removeTrailingZeros(item?.investorAmount))} <Badge ms={1} colorScheme="green">{item?.currencyCode}</Badge>
{/* {formatCurrency(removeTrailingZeros(item?.investorAmount))} */}
{parseFloat(item?.investorAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
<Badge ms={1} colorScheme="green">
{item?.currencyCode}
</Badge>
</Text>
</Box>
),
@@ -246,7 +293,7 @@ const DepositRequest = () => {
onRejectOpen();
}}
py={1}
// variant={"solid"}
// variant={"solid"}
>
<CloseIcon fontSize={"10px"} />
</Button>
@@ -329,7 +376,7 @@ const DepositRequest = () => {
isOpen={isConfirmOpen}
onClose={onConfirmClose}
id={actionId}
// firstField={firstField}
// firstField={firstField}
/>
<DepositRequestReject
isOpen={isRejectOpen}

View File

@@ -1,7 +1,10 @@
import {
Badge,
Box,
Button,
FormControl,
FormErrorMessage,
FormHelperText,
FormLabel,
Input,
Modal,
@@ -19,37 +22,50 @@ import {
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, useUpdateDepositRequestMutation } from "../../../Services/deposit.request.service";
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(),
comment: yup.string().notRequired() .max(200, "Approve Comment cannot be more than 200 characters"),
supporting_FileName: yup.mixed().required("File is required"),
// .test("fileType", "Unsupported File Format", (value) => {
// return value && FILE_TYPES.includes(value.type);
// }),
});
const DepositRequestApprove = ({ isOpen, onClose, firstField, id, data:requestData }) => {
const toast = useToast()
const DepositRequestApprove = ({
isOpen,
onClose,
firstField,
id,
data: requestData,
}) => {
const toast = useToast();
const [file, setFile] = useState();
const [isBtnLoading , setIsBtnLoading] = useState(false)
const [isBtnLoading, setIsBtnLoading] = useState(false);
const fileredData = requestData?.find((item)=> item?.id === id)
console.log(fileredData);
const [updateDepositRequest] = useUpdateDepositRequestMutation();
const { data, isLoading } = useGetDepositRequestByIdQuery(id, {
skip: !id,
});
console.log(data?.data?.investorAmount);
const [ updateDepositRequest ] = useUpdateDepositRequestMutation()
const {
const {
control,
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
@@ -57,15 +73,17 @@ const DepositRequestApprove = ({ isOpen, onClose, firstField, id, data:requestDa
});
useEffect(() => {
reset({
investorAmount:fileredData?.investorAmount
})
}, [requestData, id])
const onSubmit = async(data) => {
setIsBtnLoading(true)
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);
@@ -73,59 +91,38 @@ const DepositRequestApprove = ({ isOpen, onClose, firstField, id, data:requestDa
const file = data.supporting_FileName["0"];
formData.append("supporting_FileName", file);
try {
const res = await updateDepositRequest({ id, data: formData });
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) {}
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();
heandleOnClose();
};
const handleFileChange = (event) => {
const selectedFile = event.target.files[0];
setFile(selectedFile);
const heandleOnClose = () => {
reset();
onClose();
};
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}>
<Modal
isOpen={isOpen}
onClose={heandleOnClose}
initialFocusRef={firstField}
>
<ModalOverlay />
<ModalContent pb={4}>
@@ -136,12 +133,16 @@ const DepositRequestApprove = ({ isOpen, onClose, firstField, id, data:requestDa
) : (
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Deposit Amount (SAR)</FormLabel>
{/* <FormControl mb={4} isRequired>
<FormLabel fontSize="sm">
Deposit Amount
<Badge colorScheme="green">{data?.data?.currencyCode}</Badge>
</FormLabel>
<Input
focusBorderColor="green.400"
name="investorAmount"
{...register("investorAmount")}
value={formatCurrency(watch("investorAmount"))}
fontSize="sm"
type="number"
size="sm"
@@ -154,7 +155,24 @@ const DepositRequestApprove = ({ isOpen, onClose, firstField, id, data:requestDa
{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
@@ -175,7 +193,7 @@ const DepositRequestApprove = ({ isOpen, onClose, firstField, id, data:requestDa
)}
</FormControl>
<FormControl mb={4}>
<FormLabel fontSize="sm">Comments</FormLabel>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={5}
focusBorderColor="green.400"
@@ -184,14 +202,19 @@ const DepositRequestApprove = ({ isOpen, onClose, firstField, id, data:requestDa
fontSize="sm"
type="textarea"
size="sm"
placeholder={"Enter your comments...."}
placeholder={"Enter your comment...."}
resize={"none"}
maxLength={200}
/>
{errors.comment && (
<Text fontSize="xs" color="red">
{errors.comment.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
{watch("comment")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>

View File

@@ -2,6 +2,7 @@ import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
@@ -24,7 +25,8 @@ import { useDepositRejectMutation } from "../../../Services/deposit.request.serv
import ToastBox from "../../../Components/ToastBox";
export const conformModalSchema = yup.object().shape({
comments: yup.string().required("Comment is required"),
comments: yup.string().required("Comment is required")
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const DepositRequestReject = ({ isOpen, onClose, firstField ,id}) => {
@@ -35,6 +37,7 @@ const DepositRequestReject = ({ isOpen, onClose, firstField ,id}) => {
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
@@ -101,10 +104,10 @@ const DepositRequestReject = ({ isOpen, onClose, firstField ,id}) => {
}
return (
<Modal isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
<ModalContent pb={4}>
<ModalHeader fontSize={"md"}>Reject</ModalHeader>
<ModalHeader fontSize={"md"}>Investor Comment</ModalHeader>
<ModalCloseButton />
{isLoading ? (
<FullscreenLoaders height={"50vh"} />
@@ -121,15 +124,20 @@ const DepositRequestReject = ({ isOpen, onClose, firstField ,id}) => {
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comments...."}
placeholder={"Enter your comment...."}
rounded={"md"}
resize={"none"}
maxLength={200}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>

View File

@@ -4,79 +4,108 @@ import {
Button,
HStack,
Input,
Switch,
Text,
Tooltip,
useToast,
useDisclosure,
Link,
Text,
useBoolean,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
import { HiDotsVertical } from "react-icons/hi";
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 NormalTable from "../../../Components/DataTable/NormalTable";
import ConfirmModal from "./ConfirmModal";
import RejectModal from "./RejectModal";
import {
useDepositRejectMutation,
useGetDepositHistoryQuery,
} from "../../../Services/deposit.request.service";
import { ExternalLinkIcon } from "@chakra-ui/icons";
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
import Pagination from "../../../Components/Pagination";
import ToastBox from "../../../Components/ToastBox";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import { useGetDepositHistoryQuery } from "../../../Services/deposit.request.service";
import { debounce } from "../../Master/Sponser/AddSponser";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
import { ExternalLinkIcon } from "@chakra-ui/icons";
import InitiateReversalPopup from "../../../Components/Popups/InitiateReversalPopups";
import { generateSerialNumber, isMaker } from "../../../Constants/Constants";
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
import { useCreateBankDepositReversalRequestMutation } from "../../../Services/bankdeposit.request.service";
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 [createBankDepositReversalRequest] =
useCreateBankDepositReversalRequestMutation();
// =========================== [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 [reversalId, setReversalId] = useState();
const {
isOpen: isOpenInRev,
onOpen: onOpenInRev,
onClose: onCloseInRev,
} = useDisclosure();
const [isReversalLoading, setIsReversalLoading] = useBoolean();
// 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,
error,
refetch,
isLoading: depositHistoryLoading,
} = useGetDepositHistoryQuery({ page: currentPage, size: pageSize });
} = 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",
"Sr.no",
"Client ID",
"First Name",
"Last Name",
"Country",
"Phone Number",
"Amount in Investor currency",
"Deposit Amount",
"Deposit Date",
"Status",
"Supporting's",
isMaker() && "Reversal Action",
];
const handleUpdateStatus = debounce((id) => {
@@ -92,124 +121,126 @@ const DepositHistory = () => {
});
}, 300);
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 (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 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 (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));
return nameMatches;
})
.sort((b, a) => new Date(a.createdAt) - new Date(b.createdAt));
// const handleView = (id) => {
// setActionId(id);
// onViewOpen();
// };
const extractedArray =
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>
),
"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}
</Box>
),
"Last Name": (
<Box w={"70px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.lastName}
</Text>
),
"First Name": (
<Box isTruncated={true} w={"60px"}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.firstName}
</Text>
</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>
),
"Amount in Investor currency": (
<Box w={"100px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{/* <Badge px={2} py={1}> */}
{item?.investorAmount} <Badge ms={1} colorScheme="green">{item?.currencyCode}</Badge>
{/* </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)}
</Box>
),
Country: (
<Box w={"80px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.countryName}
</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": (
</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"}
@@ -220,57 +251,116 @@ const filteredData = data?.data?.rows
>
{/* {item?.supporting_FileName} */}
<Badge
px={2}
py={0.5}
display={"flex"}
alignItems={"center"}
textTransform={"inherit"}
fontWeight={500}
colorScheme={"forestGreen"}
>
<Link
href={"https://tanami.betadelivery.com/" + item?.supporting_FileName}
isExternal
px={2}
py={0.5}
display={"flex"}
alignItems={"center"}
textTransform={"inherit"}
fontWeight={500}
colorScheme={"forestGreen"}
>
<Box
as="span"
cursor={"pointer"}
<Link
href={import.meta.env.VITE_IMAGE_URL + item?.supporting_FileName}
isExternal
>
View
</Box>
<ExternalLinkIcon />
</Link>
{/* <Link to="www.google.com" isExternal>
<Box as="span" cursor={"pointer"}>
View
</Box>
<ExternalLinkIcon />
</Link>
{/* <Link to="www.google.com" isExternal>
<Box as="span">View</Box> <ExternalLinkIcon />
</Link> */}
</Badge>
</Badge>
</Text>
) : (
""
),
}))
"Reversal Action": (
<Box w={"120px"} isTruncated={true} cursor={"pointer"}>
{item.transactionStatus === "Approved" ? (
<Text
as={"span"}
color={!item.isReversal ? "green.500" : "#FFBB00"}
fontWeight={700}
>
{!item.isReversal ? (
<Button
onClick={() => {
onOpenInRev(); // Call the function
setReversalId(item.id);
}}
colorScheme="teal"
size="xs"
variant="outline"
>
Initiate Reversal
</Button>
) : (
"Under process"
)}
</Text>
) : (
""
)}
</Box>
),
}));
const handleDelete = () => {
const IOtype = investmentType.filter(
(investmentType) => investmentType.id !== actionId
);
// const handleDelete = () => {
// const IOtype = investmentType.filter(
// (investmentType) => investmentType.id !== actionId
// );
setTimeout(() => {
setInvestmentType(IOtype);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
// setTimeout(() => {
// setInvestmentType(IOtype);
// setDeleteAlert(false);
// setIsLoading(false);
// }, 100);
// setIsLoading(true);
// };
const handleApproved = async (data) => {
setIsReversalLoading.on(); // Start loading
try {
const { error, data: responseData } =
await createBankDepositReversalRequest({
id: reversalId,
data,
});
if (error) {
throw error; // Explicitly handle the error
}
// Success: Perform necessary actions
refetch();
toast({
render: () => (
<ToastBox message={responseData?.message || "Action successful!"} />
),
});
onCloseInRev();
} catch (error) {
// Handle errors
toast({
render: () => (
<ToastBox
message={
error?.data?.message || "Something went wrong. Please try again."
}
status="error"
/>
),
});
console.error("Error:", error);
} finally {
setIsReversalLoading.off(); // Ensure loading is toggled off
}
};
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
{/* <ConfirmModal
isOpen={isConfirmOpen}
onClose={onConfirmClose}
/>
<RejectModal
isOpen={isRejectOpen}
onClose={onRejectClose}
/> */}
<Box bg="white.500">
<HStack
display={"flex"}
@@ -293,7 +383,7 @@ const filteredData = data?.data?.rows
/>
<HStack display={"flex"} alignItems={"center"}>
<Pagination
<Pagination
isLoading={depositHistoryLoading}
pageSize={pageSize}
setPageSize={setPageSize}
@@ -317,12 +407,19 @@ const filteredData = data?.data?.rows
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
{/* <CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/> */}
<InitiateReversalPopup
onClose={onCloseInRev}
isOpen={isOpenInRev}
handelApproved={handleApproved}
isLoading={isReversalLoading}
/>
</Box>
);

View File

@@ -0,0 +1,383 @@
import {
Badge,
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
HStack,
Input,
Select,
Text,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, 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();
const [searchTerm, setSearchTerm] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
const [country, setCountry] = useState("");
const [kyc, setKyc] = useState("");
// ===========================[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
// });
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearchTerm(searchTerm.trim()); // Trim to remove leading/trailing spaces
}, 300);
return () => clearTimeout(handler);
}, [searchTerm]);
const {
data: investorDetails,
isLoading: investorDetailsLoading,
refetch,
} = useGetUnbanInvestorQuery(
{
page: 1, // Omit pagination for search
size: 10000, // Omit pagination for search
// page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
// size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
search: debouncedSearchTerm,
country_xid: country,
KYCStatus: kyc,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
}
);
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" : "Not Completed"}
</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>
<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 className="col" justifyContent={"end"}>
<Select
w={250}
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">Bahrain</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>
<Select
w={250}
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">Not Completed</option>
<option value="1">Completed</option>
</Select>
</HStack>
</HStack>
<Box overflow={"scroll"} h={"58vh"}>
<NormalTable
centered={true}
emptyMessage={`We don't have any Sponsors`}
tableHeadRow={tableHeadRow}
data={extractedArray}
setSelectedRadio={setSelectedRadio}
selectedRadio={selectedRadio}
showRadioButton={true}
/>
</Box>
<HStack justifyContent={"flex-end"} px={2}>
<Button
rightIcon={<EmailIcon />}
rounded={"sm"}
size={"sm"}
colorScheme="forestGreen"
type="submit"
isLoading={isLoading}
onClick={handleSend}
>
Send Email
</Button>
</HStack>
</Box>
);
};
export default EmailNotification;

View File

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

View File

@@ -1,57 +1,74 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Menu,
MenuButton,
MenuItem,
MenuList,
Portal,
Select,
Switch,
Tag,
Text,
Tooltip,
useDisclosure,
useToast,
useToast
} from "@chakra-ui/react";
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 GlobalStateContext from "../../Contexts/GlobalStateContext";
import { debounce } from "../Master/Sponser/AddSponser";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import DataTable from "../../Components/DataTable/DataTable";
import React, { useContext, useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import CustomAlertDialog from "../../Components/CustomAlertDialog";
import NormalTable from "../../Components/DataTable/NormalTable";
import Pagination from "../../Components/Pagination";
import ToastBox from "../../Components/ToastBox";
import GlobalStateContext from "../../Contexts/GlobalStateContext";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import { debounce } from "../Master/Sponser/AddSponser";
// import InvestmentDetailsEdit from "./InvestmentDetailsEdit";
import { generateSerialNumber } from "../../Constants/Constants";
import { TABLE_PAGINATION } from "../../Constants/Paginations";
import { useGetInvestorsQuery } from "../../Services/investor.details.service";
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 +82,22 @@ const BankInvestor = () => {
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr N/O",
"Sr No",
"Client ID",
"First Name",
"Last Name",
"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 +106,17 @@ 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 +132,102 @@ 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>
),
"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 +249,8 @@ 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 +264,7 @@ const BankInvestor = () => {
spacing="24px"
>
<Input
mt={1}
mt={1}
type="search"
width={300}
placeholder="Search..."
@@ -218,17 +274,38 @@ 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 +323,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,442 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Link,
Text,
Tooltip,
useBoolean,
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, isMaker } 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";
import InitiateReversalPopup from "../../../Components/Popups/InitiateReversalPopups";
import ToastBox from "../../../Components/ToastBox";
import { useCreateFawateerReversalRequestMutation } from "../../../Services/reversal.fawateer.deposit.service";
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 [createFawateerReversalRequest] =
useCreateFawateerReversalRequestMutation();
const [reversalId, setReversalId] = useState();
const {
isOpen: isOpenInRev,
onOpen: onOpenInRev,
onClose: onCloseInRev,
} = useDisclosure();
const [isReversalLoading, setIsReversalLoading] = useBoolean();
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
}
);
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",
isMaker() && "Reversal Action",
];
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>
),
"Reversal Action": (
<Box w={"120px"} isTruncated={true} cursor={"pointer"}>
{item.transactionStatus === "Approved" ? (
<Text
as={"span"}
color={!item.isReversal ? "green.500" : "#FFBB00"}
fontWeight={700}
>
{!item.isReversal ? (
<Button
onClick={() => {
onOpenInRev(); // Call the function
// setReversalId(item.transaction_xid);
setReversalId(item.id);
}}
colorScheme="teal"
size="xs"
variant="outline"
>
Initiate Reversal
</Button>
) : (
"Under process"
)}
</Text>
) : (
""
)}
</Box>
),
}));
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setSponser(updatedSponsors);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const handleApproved = async (data) => {
setIsReversalLoading.on(); // Start loading
try {
const { error, data: responseData } = await createFawateerReversalRequest(
{
id: reversalId,
data,
}
);
if (error) {
throw error; // Explicitly handle the error
}
// Success: Perform necessary actions
refetch();
toast({
render: () => (
<ToastBox message={responseData?.message || "Action successful!"} />
),
});
onCloseInRev();
} catch (error) {
// Handle errors
toast({
render: () => (
<ToastBox
message={
error?.data?.message || "Something went wrong. Please try again."
}
status="error"
/>
),
});
console.error("Error:", error);
} finally {
setIsReversalLoading.off(); // Ensure loading is toggled off
}
};
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}
/>
<InitiateReversalPopup
onClose={onCloseInRev}
isOpen={isOpenInRev}
handelApproved={handleApproved}
isLoading={isReversalLoading}
/>
</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,183 @@
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]);
useEffect(() => {
if (!isOpen) {
reset();
}
}, [isOpen, 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 checker Comment...."}
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,189 @@
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 { useRejectCommentMutation } from "../../../Services/fawateer.request.service";
// export const conformModalSchema = yup.object().shape({
// comments: yup.string().required("Comment is required")
// .max(200, "Approve Comment cannot be more than 200 characters"),
// });
export const conformModalSchema = yup.object().shape({
// checkerComment: yup.string().required("Comment is required")
// .max(50, "Investment name cannot be more than 50 characters"),
comments: yup
.string()
.required("Comment is required")
.max(200, "Approve Comment cannot be more than 200 characters"),
});
const RequestRejectModal = ({ isOpen, onClose, firstField ,id}) => {
const [isBtnLoading , setIsBtnLoading] = useState(false)
const toast = useToast()
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
useEffect(() => {
if (!isOpen) {
reset(); // Clear the form state
}
}, [isOpen, reset]);
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"}
maxLength={200}
/>
{errors.comments && (
<Text fontSize="xs" color="red">
{errors.comments.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
Maximum length should be 200 characters. You have entered
<Text as={'span'} ml={2}>{watch("comments")?.length || 0} </Text>characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
colorScheme="gray"
mr={3}
onClick={onClose}
size={"sm"}
rounded={"sm"}
>
Cancel
</Button>
<Button
colorScheme="forestGreen"
variant="solid"
size={"sm"}
rounded={"sm"}
isLoading={isBtnLoading}
type="submit"
>
Send
</Button>
</ModalFooter>
</Box>
)}
</ModalContent>
</Modal>
);
};
export default RequestRejectModal;

View File

@@ -0,0 +1,138 @@
import {
Button,
DrawerFooter,
FormControl,
FormErrorMessage,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalOverlay,
Stack,
useToast,
} from "@chakra-ui/react";
import * as yup from "yup";
import React, { useState } from "react";
import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForgetPasswordMutation } from "../Services/forget.password.service";
import ToastBox from "../Components/ToastBox";
const validationSchema = yup.object().shape({
emailAddress: yup
.string()
.email("Invalid email format")
.required("Email address is required")
.min(6, "Email address must be at least 6 characters long")
.max(255, "Email address can be at most 255 characters long"),
});
const ForgetPassword = ({ isOpen, onClose, firstField }) => {
const toast = useToast();
const [isLoading, setIsLoading] = useState(false);
const [forgetPassword] = useForgetPasswordMutation();
const {
control,
handleSubmit,
formState: { errors },
reset, // Add reset from useForm
} = useForm({
resolver: yupResolver(validationSchema),
});
const onSubmit = async (formData) => {
setIsLoading(true);
try {
const res = await forgetPassword(formData);
if (res?.data?.statusCode === 200) {
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.error(error);
} finally {
setIsLoading(false);
}
};
const handleClose = () => {
setIsLoading(false);
onClose();
reset(); // Reset form state when modal closes
};
return (
<Modal
isCentered
isOpen={isOpen}
onClose={handleClose}
initialFocusRef={firstField}
>
<ModalOverlay />
<ModalContent>
<form onSubmit={handleSubmit(onSubmit)}>
<ModalHeader fontSize="md">Forgot Password</ModalHeader>
<ModalCloseButton />
<ModalBody pb={4}>
<Stack spacing={4}>
<FormControl isInvalid={errors.emailAddress} isRequired>
<FormLabel fontSize="sm" mb={3} fontWeight={500}>
Please Enter Email Address
</FormLabel>
<Controller
name="emailAddress"
control={control}
render={({ field }) => (
<Input
{...field}
size="md"
fontSize="sm"
focusBorderColor="forestGreen.300"
rounded={4}
type="text"
/>
)}
/>
<FormErrorMessage fontSize="xs" fontWeight={500}>
{errors.emailAddress?.message}
</FormErrorMessage>
</FormControl>
</Stack>
</ModalBody>
<DrawerFooter mb={5}>
<Button
w="100%"
colorScheme="forestGreen"
rounded="md"
size="md"
type="submit"
isLoading={isLoading}
fontWeight={400}
fontSize="sm"
>
Reset Password
</Button>
</DrawerFooter>
</form>
</ModalContent>
</Modal>
);
};
export default ForgetPassword;

View File

@@ -30,14 +30,14 @@ import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import CurrencyInput from "../../../Components/CurrencyInput";
const cashDetails = 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"),
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 params = useParams()
const id = params?.id
const [file, setFile] = useState("");
const [fileName, setFileName] = useState("");
@@ -56,7 +56,7 @@ const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, da
const [updateVideoArtifacts] = useUpdateVideoArtifactsMutation()
// const {
// data
// } = useGetArtifactsQuery(id)
// } = useGetArtifactsQuery(id)
const {
control,
@@ -91,11 +91,11 @@ const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, da
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
});
}
} catch (error) {
console.log(error);
setIsLoading(false);
}
};
@@ -113,7 +113,9 @@ const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, da
const handleClose = () => {
setAlert(false)
onClose()
reset()
reset({
transactionAmount:""
})
}
return (
@@ -132,13 +134,14 @@ const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, da
<DrawerBody>
<Stack spacing={4}>
<FormControl isInvalid={errors.transactionDate} isRequired>
<FormControl isInvalid={errors.transactionDate} isRequired>
<FormLabel fontSize={"sm"}>Date Selection</FormLabel>
<Controller
name="transactionDate"
control={control}
render={({ field }) => (
<Input {...field} fontSize={"sm"} type="date" size={"sm"} />
<Input
focusBorderColor="forestGreen.300" {...field} fontSize={"sm"} type="date" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
@@ -158,6 +161,7 @@ const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, da
placeholder="Select an option"
fontSize={"sm"}
size={"sm"}
focusBorderColor="forestGreen.300"
>
{IODetails?.ioCashTransaction?.map(({ id, transactionName }) => (
<option key={id} value={id}>
@@ -197,7 +201,8 @@ const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, da
name="comments"
control={control}
render={({ field }) => (
<Textarea {...field} textAlign={'right'} fontSize={"sm"} type="text" size={"sm"} />
<Textarea {...field} textAlign={'left'}
focusBorderColor="forestGreen.300" fontSize={"sm"} type="text" size={"sm"} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>

View File

@@ -11,10 +11,13 @@ import {
FormControl,
FormErrorMessage,
FormLabel,
HStack,
Input,
Select,
Stack,
Text,
Textarea,
VStack,
useToast,
} from "@chakra-ui/react";
import * as yup from "yup";
@@ -31,8 +34,8 @@ import {
import { formatDatee } from "../../../Components/FormField";
const ioNav = yup.object().shape({
transactionDate: yup.string().required("Artifact name is required"),
transactionAmount: yup.number().required("Artifact name is required"),
transactionDate: yup.string().required("Date is required"),
transactionAmount: yup.number().required("New NAV is required"),
comments: yup.string().notRequired(),
});
@@ -85,10 +88,10 @@ import { formatDatee } from "../../../Components/FormField";
});
handleClose()
}else if(res?.error?.status === 400){
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
});
handleClose()
}
@@ -110,6 +113,7 @@ import { formatDatee } from "../../../Components/FormField";
};
const handleClose = () => {
setIsLoading(false);
setAlert(false)
onClose()
reset({
@@ -122,7 +126,15 @@ import { formatDatee } from "../../../Components/FormField";
const today = formatDatee(new Date(), 'yyyy-MM-dd');
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));
@@ -161,7 +173,7 @@ const today = formatDatee(new Date(), 'yyyy-MM-dd');
<FormControl isInvalid={errors.transactionAmount} isRequired>
<FormLabel fontSize={"sm"}>Transaction Amount</FormLabel>
<FormLabel fontSize={"sm"}>New NAV</FormLabel>
<Controller
name="transactionAmount"
control={control}
@@ -173,7 +185,26 @@ const today = formatDatee(new Date(), 'yyyy-MM-dd');
{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}>

View File

@@ -8,21 +8,22 @@ import IODetails from "./IODetails";
import KeyMerits from "./KeyMerits";
import IOArtifacts from "./IOArtifacts";
import Investors from "./Investors";
import IOCashDetails from "./IOCashDetails";
import IONAVDetails from "./IONAVDetails";
// import IOCashDetails from "./IOCashDetailsold";
// import IONAVDetails from "./IONAVDetailsOld";
import InvestmentDocument from "./InvestmentDocument"; // Ensure this is the correct import
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";
import IOCashDetails from "./IOCashDetails/IOCashDetails";
import IONAVDetails from "./IONAVDetails/IONAVDetails";
import IOTransaction from "./IOTransaction/IOTransaction";
const CreateIO = () => {
const id = useParams()?.id;
const { data, error, isLoading } = useGetIOprepopulateDataQuery();
// console.log(data?.data);
const enableNextTab = (index) => {
setTabs((prevTabs) => {
@@ -44,38 +45,43 @@ 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,
Content: UnderConstruction,
isDisabled: id ? false : true,
Content: Investors,
// Content: UnderConstruction,
isDisabled: id ? true : true,
},
{
label: "IO Cash Detail",
Content: IOCashDetails,
isDisabled: id ? false : true,
isDisabled: id ? true : true,
},
{
label: "IO NAV Details",
Content: IONAVDetails,
isDisabled: id ? false : true,
isDisabled: id ? true : true,
},
{
label: "Distribution to Investors",
Content: IONAVDetails,
isDisabled: id ? false : true,
Content: Destribution,
isDisabled: id ? true : true,
},
{
label: "IO Transaction",
Content: IOTransaction,
isDisabled: id ? true : true,
},
];
@@ -116,7 +122,8 @@ const CreateIO = () => {
<Tab
isDisabled={isDisabled}
key={index}
fontSize={"sm"}
fontSize={"xs"}
fontWeight={500}
_selected={{
color: "#004118",
borderBottom: "2px solid #38a169",

View File

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

View File

@@ -22,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,
@@ -32,6 +32,8 @@ import {
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()
@@ -114,7 +116,7 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
setDeleteAlertVideo(false);
setIsLoadingBtn(false);
toast({
render: () => <ToastBox message={res?.data?.message} status="error" />,
render: () => <ToastBox message={res?.data?.message} status="success" />,
});
}
} catch (error) {
@@ -132,7 +134,7 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
setDeleteAlertImage(false);
setIsLoadingBtn(false);
toast({
render: () => <ToastBox message={res?.data?.message} status="error" />,
render: () => <ToastBox message={res?.data?.message} status="success" />,
});
}
@@ -143,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"}
@@ -180,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
@@ -246,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",
@@ -253,7 +273,7 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
"Action",
];
const extractedArrayTwo = IObyID?.data?.artifactsVideo?.map(
const extractedArrayTwo = sortedDataVideo?.map(
(item, index) => ({
"Sr.no": (
<Text
@@ -352,9 +372,9 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
Manage IO Images
</Box>
<HStack>
{IObyID?.data?.artifactsImage?.length !== 0 &&<SetDisplayOrder data={IObyID?.data?.artifactsImage} />}
{IObyID?.data?.artifactsImage?.length !== 0 &&<SetDisplayOrderIOArtifactsImages data={sortedDataImage} />}
<Button
leftIcon={<AddIcon />}
onClick={onOpen}
@@ -390,7 +410,7 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
Manage IO videos
</Box>
<HStack>
{IObyID?.data?.artifactsVideo?.length !== 0 &&<SetDisplayOrder data={IObyID?.data?.artifactsVideo} />}
{IObyID?.data?.artifactsVideo?.length !== 0 &&<SetDisplayOrderIOArtifactsVideo data={sortedDataVideo} />}
<Button
leftIcon={<AddIcon />}
onClick={onOpenVideo}

View File

@@ -10,6 +10,7 @@ import {
DrawerOverlay,
FormControl,
FormErrorMessage,
FormHelperText,
FormLabel,
Input,
Stack,
@@ -26,11 +27,11 @@ import { useParams } from "react-router-dom";
import ToastBox from "../../../Components/ToastBox";
const investmentVideoSchema = yup.object().shape({
artifactName: yup.string().required("Artifact name is required"),
artifactName: yup.string().required("Artifact name is required")
.max(50, "Approve Comment cannot be more than 50 characters"),
artifactStreamingURL: yup.string()
.required("Artifact streaming URL is required")
.url("Invalid URL format")
.matches(/\.mp4$/, "URL must end with .mp4"),
.url("Invalid URL format"),
});
const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, data }) => {
@@ -153,21 +154,25 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, da
<DrawerBody>
<Stack spacing={4}>
<FormControl isInvalid={errors.artifactName}>
<FormControl isInvalid={errors.artifactName} isRequired>
<FormLabel fontSize={"sm"}>Artifact Name</FormLabel>
<Controller
name="artifactName"
control={control}
render={({ field }) => (
<Input {...field} fontSize={"sm"} type="text" size={"sm"} />
<Input {...field} fontSize={"sm"} type="text" size={"sm"} maxLength={50} />
)}
/>
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
{errors.artifactName?.message}
</FormErrorMessage>
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 50 characters. You have entered </Box>
{watch("artifactName")?.length || 0} characters.
</FormHelperText>
</FormControl>
<FormControl isInvalid={errors.artifactStreamingURL}>
<FormControl isInvalid={errors.artifactStreamingURL} isRequired>
<FormLabel fontSize={"sm"}>Artifact Streaming URL</FormLabel>
<Controller
name="artifactStreamingURL"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
@@ -23,14 +24,17 @@ 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 IOCashDetailsOld = () => {
const toast = useToast();
const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const { caseDetails, setCaseDetails, IODetails } = useContext(GlobalStateContext);
const { caseDetails, setCaseDetails, IODetails } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
@@ -48,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,
@@ -76,17 +88,18 @@ const IOCashDetails = () => {
}, 300);
// Table filter
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 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 = filteredData?.map((item, index) => ({
id: item?.id,
"Date": (
Date: (
<Text
justifyContent={"center"}
as={"span"}
@@ -94,12 +107,12 @@ const IOCashDetails = () => {
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item?.transactionDate}
{formatDate(item?.transactionDate)}
</Text>
),
"Transaction type": (
<Text
justifyContent ={"center"}
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
@@ -108,20 +121,27 @@ const IOCashDetails = () => {
{item?.transactionType}
</Text>
),
"Amount": (
Amount: (
<Text
justifyContent ={"center"}
justifyContent={"left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{`$${parseFloat(item.transactionAmount||0).toLocaleString()}`}
<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 ={"center"}
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
@@ -132,30 +152,53 @@ const IOCashDetails = () => {
),
"Update by ": (
<Text
justifyContent ={"center"}
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
gap={2}
className="d-flex align-items-center web-text-small"
>
<Avatar size='sm' name={item.creator?.firstName} src={item.creator?.profilePhoto} />{item.creator?.firstName}
<Avatar
size="sm"
name={item.creator?.firstName}
src={item.creator?.profilePhoto}
/>
{item.creator?.firstName}
</Text>
),
"Update On": (
<Text
justifyContent ={"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(
(sponsor) => sponsor.id !== actionId
@@ -173,7 +216,7 @@ const IOCashDetails = () => {
return (
<Table size="sm">
<Tbody backgroundColor="gray.50">
<Tr >
<Tr>
<Th
textAlign={"center"}
p={3}
@@ -267,9 +310,35 @@ const IOCashDetails = () => {
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
<Button
onClick={() =>
exportToExcelNew(ioCashExporteDetails, "IO Cash History")
}
leftIcon={<LuFileSpreadsheet />}
colorScheme="forestGreen"
size={"sm"}
variant={"outline"}
rounded={"sm"}
fontSize={"xs"}
isDisabled={ioCashExporteDetails?.length === 0}
>
Export xls
</Button>
{IODetails?.isInvestedAmount ? <Button onClick={onOpen} leftIcon={<AddIcon/>} colorScheme="forestGreen" size={'sm'} rounded={'sm'} fontSize={'xs'} >Add IO Cash</Button>:null}
{IODetails?.isInvestedAmount ? (
<Button
onClick={onOpen}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add IO Cash
</Button>
) : null}
</HStack>
</HStack>
</Box>
@@ -277,7 +346,7 @@ const IOCashDetails = () => {
centered={true}
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
@@ -294,25 +363,13 @@ const IOCashDetails = () => {
isLoading={isLoading}
/>
<AddCashDetails
isOpen={isOpen}
onClose={onClose}
firstField={firstField} />
<AddCashDetails
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
</Box>
);
};
export default IOCashDetails;
export default IOCashDetailsOld;

View File

@@ -6,35 +6,18 @@ 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,
useUpdateIOMutation,
} 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 { useToast } from "@chakra-ui/react";
import { formatDatee } from "../../../Components/FormField";
import { removeTrailingZeros } from "../../../Constants/Constants";
import {
formatDateToYYYYMMDD,
removeTrailingZeros,
} from "../../../Constants/Constants";
const schema = yup.object().shape({
investmentNameEnglish: yup
@@ -60,16 +43,15 @@ 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()
.typeError("Goal Amount is must be number")
.required('Goal amount is required')
.positive('Goal amount must be a positive number'),
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"),
closingDate: yup
.date()
.notRequired("Closing date is required")
@@ -88,31 +70,35 @@ const schema = yup.object().shape({
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
comment: yup
.string()
.required("Expected return is required"),
.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 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 ]
@@ -132,6 +118,7 @@ const IODetails = ({ enableNextTab, index, data }) => {
isLoading: IObyIDisLoading,
error: IObyIDerror,
} = useGetIOByIdQuery(id, { skip: !id });
console.log(IObyID);
const [creatIO] = useCreateIOMutation();
const [updateIO] = useUpdateIOMutation();
@@ -154,9 +141,12 @@ const IODetails = ({ enableNextTab, index, data }) => {
});
const miniValue = data?.country?.map(
({ countryName, flagIcon, minInvestmentAmt, countryCode, id, currency }, index) => {
(
{ countryName, flagIcon, minInvestmentAmt, countryCode, id, currency },
index
) => {
return {
id:id,
id: id,
country: countryName,
value: minInvestmentAmt,
logo: flagIcon,
@@ -165,24 +155,87 @@ const IODetails = ({ enableNextTab, index, data }) => {
}
);
const minInvestmentById = IObyID?.data?.minInvestmentAmt?.map(({minInvestmentAmt, country, country_xid, })=>{
return{
id:country_xid,
country: country?.countryName,
value: removeTrailingZeros(minInvestmentAmt),
logo: country?.flagIcon,
curr: country?.countryCode,
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: removeTrailingZeros(minInvestmentAmt),
logo: country?.flagIcon,
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"),
holdingPeriod: yup.string().required("Holding period is required"),
holdingPeriodArabic: yup.string().required("Holding period is required"),
isShariah: yup.string().required("CheckBox is required"),
// minInvestmentAmount: yup
// .number()
// .required("Minimum investment is required")
// .positive("Minimum investment must be a positive number")
// .min(1, "Minimum investment must be at least 1"),
ISIN: yup.string().notRequired(),
InvestmentDetails: yup.string().notRequired(),
comment: yup
.string()
.notRequired()
.min(10, "Comment must be at least 10 characters long")
.max(100, "Comment must be at most 100 characters long"),
expectedReturn: yup.string().required("Expected return is required"),
});
const [values, setValues] = useState(id ? minInvestmentById : miniValue);
const [values, setValues] = useState(id?minInvestmentById:miniValue);
const formatNumber = (num) => {
// Remove non-numeric characters and format with commas
return num.replace(/\D/g, '')
.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return num.replace(/\D/g, "").replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
// console.log(values);
@@ -195,11 +248,12 @@ const IODetails = ({ enableNextTab, index, data }) => {
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(schema),
resolver: yupResolver(id ? schemaEdit : schema),
mode: "all",
});
useEffect(() => {
setIOloading(IObyIDisLoading)
setIOloading(IObyIDisLoading);
setIODetails({
...IObyID?.data,
});
@@ -220,17 +274,11 @@ const IODetails = ({ enableNextTab, index, data }) => {
minInvestmentAmount: IObyID?.data?.minInvestmentAmount,
holdingPeriodArabic: IObyID?.data?.minInvestmentAmount,
expectedReturnArabic: IObyID?.data?.minInvestmentAmount,
isShariah: IObyID?.data?.isShariah,
});
}
}, [id, IObyID]);
// const minInvestmentById =
//=======================[ Creator ]
const formFields = [
{
@@ -241,8 +289,10 @@ 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.`
maxLength: 150,
helperText: `Maximum length should be 150 characters. You have entered ${
watch()?.investmentNameEnglish?.length || 0
} characters.`,
},
{
label: "IO Name (Arabic)",
@@ -253,8 +303,10 @@ 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.`
maxLength: 150,
helperText: `Maximum length should be 150 characters. You have entered ${
watch()?.investmentNameArabic?.length || 0
} characters.`,
},
{
label: "Description",
@@ -264,8 +316,10 @@ const IODetails = ({ enableNextTab, index, data }) => {
isRequired: true,
section: " ",
width: "49%",
maxLength:1000,
helperText:`Maximum length should be 1000 characters. You have entered ${watch()?.descriptionEnglish?.length || 0} characters.`
maxLength: 1000,
helperText: `Maximum length should be 1000 characters. You have entered ${
watch()?.descriptionEnglish?.length || 0
} characters.`,
},
{
label: "Description (Arabic)",
@@ -276,9 +330,10 @@ 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.`
maxLength: 1000,
helperText: `Maximum length should be 1000 characters. You have entered ${
watch()?.descriptionArabic?.length || 0
} characters.`,
},
{
label: "Holding Period",
@@ -289,8 +344,10 @@ const IODetails = ({ enableNextTab, index, data }) => {
section: " ",
width: "49%",
value: IObyID?.data?.holdingPeriod,
maxLength:20,
helperText:`Maximum length should be 20 characters. You have entered ${watch()?.holdingPeriod?.length || 0} characters.`
maxLength: 20,
helperText: `Maximum length should be 20 characters. You have entered ${
watch()?.holdingPeriod?.length || 0
} characters.`,
},
{
label: "Holding Period (Arabic)",
@@ -302,18 +359,19 @@ const IODetails = ({ enableNextTab, index, data }) => {
section: " ",
width: "49%",
value: IObyID?.data?.holdingPeriodArabic,
maxLength:20,
helperText:`Maximum length should be 20 characters. You have entered ${watch()?.holdingPeriodArabic?.length || 0} characters.`
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: "49%",
width: "32.3%",
value: IObyID?.data?.expectedReturn,
},
@@ -324,10 +382,19 @@ const IODetails = ({ enableNextTab, index, data }) => {
isRequired: true,
arabic: true,
section: " ",
width: "49%",
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",
@@ -366,18 +433,19 @@ const IODetails = ({ enableNextTab, index, data }) => {
name: "closingDate",
// value: "IObyID?.data?.closingDate",
type: "date",
isRequired: true,
// isRequired: true,
section: " ",
width: "32.3%",
dateValue:formatDatee(IObyID?.data?.closingDate),
dateValue: formatDatee(IObyID?.data?.closingDate),
// helperText: IObyID && `Current closing date is : ${formatDate(IObyID?.data?.closingDate)}`
closingDate:id ? null : true
},
{
label: "ISIN",
placeHolder: "",
name: "ISIN",
type: "text",
align:"right",
align: "right",
section: " ",
width: "32.3%",
},
@@ -389,6 +457,10 @@ 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.`,
},
{
@@ -400,7 +472,7 @@ const IODetails = ({ enableNextTab, index, data }) => {
width: "100%",
isRequired: true,
options: investmentTypeOptions,
handleInputChange: handleInputChange,
handleInputChange: id ? handleInputChangeEdit : handleInputChangeCreate,
value: values,
},
@@ -413,155 +485,12 @@ const IODetails = ({ enableNextTab, index, data }) => {
width: "100%",
options: investmentTypeOptions,
value: IObyID?.data?.comment,
maxLength:100,
helperText:`Maximum length should be 100 characters. You have entered ${watch()?.comment?.length || 0} characters.`
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]) {
@@ -573,26 +502,28 @@ const IODetails = ({ enableNextTab, index, data }) => {
const onSubmit = async (data) => {
delete data.table;
setIsLoading(true);
// console.log(data);
const updatedMinAmount = values?.map(({id, value})=>{
const updatedMinAmount = values?.map(({ id, value, _id }) => {
return {
country_xid:id,
minInvestmentAmt: Number(value)
}
})
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) {
@@ -602,14 +533,24 @@ const IODetails = ({ enableNextTab, index, data }) => {
});
navigate(`/view-io/${id}`);
enableNextTab(index);
} else if(res?.error?.status === 400){
} else if (res?.error?.status === 400) {
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
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"} />
),
});
}
} else {
try {
console.log("========================", formData);
const res = await creatIO(formData);
console.log(res?.error?.status);
if (res?.data?.statusCode === 200) {
@@ -619,15 +560,19 @@ const IODetails = ({ enableNextTab, index, data }) => {
});
navigate(`/view-io/${res?.data?.data}`);
enableNextTab(index);
} else if(res?.error?.status === 400){
} else if (res?.error?.status === 400) {
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} />
),
});
}else if(res?.error?.status === 500){
} else if (res?.error?.status === 500) {
setIsLoading(false);
toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} />
),
});
}
} catch (error) {
@@ -636,7 +581,6 @@ const IODetails = ({ enableNextTab, index, data }) => {
}
}
// ==========================
// if (params?.id) {
// return enableNextTab(index);
@@ -654,9 +598,8 @@ const IODetails = ({ enableNextTab, index, data }) => {
};
return IObyIDisLoading ? (
<FullscreenLoaders height={'70vh'} />
<FullscreenLoaders height={"70vh"} />
) : (
<FormInputMain
p={0.1}
w={250}
@@ -667,7 +610,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,225 +0,0 @@
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 } 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 IONAVDetails = () => {
const { navDetails, setNavDetails, IODetails } =
useContext(GlobalStateContext);
const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
console.log(IODetails?.ioNAVHistory);
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
// Table setup
const tableHeadRow = [
// "Sr.No",
"Valuation Date",
"NAV",
"Last NAV update",
"Investment Closed",
"Comments",
"Update by ",
// "Update On",
];
// Table filter
const filteredData = IODetails?.ioNAVHistory?.filter((item) => {
const name = item.transactionType;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches;
}).sort((b, a) => new Date(a.transactionDate) - new Date(b.transactionDate));
const extractedArray=filteredData?.map((item, index) => ({
id: item?.id,
"Sr.No": index +1,
"Valuation Date": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{formatDatee(item.transactionDate)}
</Text>
),
"NAV": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{/* {`${item.transactionAmount}`} */}
{`$${parseFloat(item.transactionAmount||0).toLocaleString()}`}
</Text>
),
"Last NAV update": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.previousNAVvalue && `${item.previousNAVvalue}`}
</Text>
),
"Investment Closed": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.initialNAVvalue&& `${item.initialNAVvalue}`}
</Text>
),
"Comments": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.comments}
</Text>
),
"Update by ": (
<Text
justifyContent ={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
gap={2}
className="d-flex align-items-center web-text-small"
>
<Avatar size='sm' name={"faisal"} src={null} />Faisal
</Text>
),
"Update On": (
<Text
justifyContent={"center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.updateOn}
</Text>
),
}));
const handleDelete = () => {
const updatedNav = navDetails.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setNavDetails(updatedNav);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
return (<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
{/* <HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} />
</HStack> */}
{IODetails?.isInvestedAmount ? <Button onClick={onOpen} leftIcon={<AddIcon/>} colorScheme="forestGreen" size={'sm'} rounded={'sm'} fontSize={'xs'} >Add IO NAV</Button>:null}
</HStack>
</Box>
<NormalTable
centered={true}
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
caption={"Tanami v1.0"}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<AddIONav
isOpen={isOpen}
onClose={onClose}
firstField={firstField} />
</Box>
)
}
export default IONAVDetails

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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