Compare commits

...

139 Commits

Author SHA1 Message Date
Swapnil Bendal
b141743190 Merge remote-tracking branch 'origin/main' into Sprint-10 2025-01-22 13:16:43 +05:30
Swapnil Bendal
f9d0b3ce6c Merge remote-tracking branch 'origin/dev' into Sprint-10 2025-01-22 13:16:30 +05:30
Swapnil Bendal
965263a3f2 Merge remote-tracking branch 'origin/sprint10/reversal' into Sprint-10 2025-01-21 16:47:09 +05:30
YasinShaikh123
04a5d1280f approve history maker [ Search ] 2025-01-21 16:44:01 +05:30
YasinShaikh123
d40eadb35e [ Approve Historty maker search ] 2025-01-21 16:42:57 +05:30
YasinShaikh123
f4a2fd2889 [ update amount reseversl ] 2025-01-21 16:25:38 +05:30
Swapnil Bendal
96f813f632 Merge branch 'sprint10/reversal' into Sprint-10 2025-01-21 15:31:16 +05:30
YasinShaikh123
8587b91a33 change Reversal heading 2025-01-21 15:15:48 +05:30
YasinShaikh123
49b06e81a6 [ Add ISDcode ] 2025-01-21 12:42:02 +05:30
Swapnil Bendal
79ec8d92ae Implement debounced search functionality in ApproveHistory component and update API query structure 2025-01-20 20:38:52 +05:30
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
35d3e07224 Merge pull request 'dev' (#31) from dev into main
Reviewed-on: #31
2025-01-10 13:34:54 +00:00
Swapnil Bendal
651c775c2a Merge remote-tracking branch 'origin/main' into dev 2025-01-10 19:02:33 +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
6c2a38becb Merge pull request 'bug-fix/9.0.3' (#30) from bug-fix/9.0.3 into main
Reviewed-on: #30
2024-12-24 12:47:20 +00:00
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
bddf7381a6 Merge pull request '[fixed] - pending action' (#29) from dev into main
Reviewed-on: #29
2024-12-24 11:07:30 +00:00
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
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
102 changed files with 8610 additions and 2825 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

@@ -63,8 +63,8 @@ const App = () => {
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} />
) : (

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

@@ -58,7 +58,6 @@ const NormalTable = ({
};
const handleCheckboxChange = (value) => {
if (radio) {
// If radio is true, select only one option

View File

@@ -520,7 +520,7 @@ const FormField = ({
ps={1}
{...field}
{...props} size='md' colorScheme='forestGreen'>
<Text as={"span"} fontSize={"sm"}>Is This Sharia Compliant</Text>
<Text as={"span"} fontSize={"sm"}>Is This Shariah Compliant</Text>
</Checkbox>
</HStack>
);} else{

View File

@@ -13,8 +13,9 @@ 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";
@@ -23,6 +24,7 @@ import GlobalStateContext from "../Contexts/GlobalStateContext";
import { MdOutlineDarkMode, MdOutlineLightMode } from "react-icons/md";
import logoMini from "../assets/propic.png";
import { BsBack } from "react-icons/bs";
import ChangePassword from "../Pages/ChangePassword";
const HeaderMain = ({
link,
@@ -35,6 +37,8 @@ const HeaderMain = ({
}) => {
const navigate = useNavigate();
const { colorMode, toggleColorMode } = useContext(GlobalStateContext);
const { isOpen, onOpen, onClose } = useDisclosure();
const firstField = useRef();
return (
<Box
@@ -66,11 +70,11 @@ const HeaderMain = ({
<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"
@@ -112,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

@@ -5,7 +5,7 @@ import { ChevronLeftIcon, ChevronRightIcon } from "@chakra-ui/icons";
const Pagination = ({
pageSize,
setPageSize,
totalItems,
totalItems = 1,
isLoading,
setCurrentPage,
currentPage,
@@ -49,7 +49,7 @@ const Pagination = ({
value={pageSize}
onChange={handlePageSizeChange}
>
{[15, 20, 30]?.map((size) => (
{[15, 20, 30, 500]?.map((size) => (
<option key={size} value={size}>
{size}
</option>
@@ -84,7 +84,7 @@ const Pagination = ({
onClick={paginationNext}
className="link pointer"
isDisabled={currentPage === totalPages}
aria-label="Next Page"
aria-label="Next Page"
/>
</HStack>
</HStack>

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,10 +1,9 @@
import dns from "node:dns"
import * as XLSX from 'xlsx';
import CryptoJS from "crypto-js";
export const generateSerialNumber = (index, currentPage, pageSize) => {
export const generateSerialNumber = (index, currentPage = 1, pageSize = 1) => {
return (currentPage - 1) * pageSize + (index + 1);
};
@@ -12,7 +11,7 @@ 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];
}
@@ -34,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();
@@ -57,7 +56,7 @@ export function removeTrailingZeros(value) {
const remainingMinutes = minutes % 60;
const remainingSeconds = seconds % 60;
return `${remainingDays === 0 ? "": remainingDays+"d"} ${remainingHours === 0 ? "": remainingHours+"h"} ${remainingMinutes}m ${remainingSeconds}s `;
return `${remainingDays === 0 ? "" : remainingDays + "d"} ${remainingHours === 0 ? "" : remainingHours + "h"} ${remainingMinutes}m ${remainingSeconds}s `;
}
@@ -95,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
}
}
@@ -126,15 +128,15 @@ 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);
}
@@ -164,7 +166,7 @@ const getNestedValue = (obj, key) => {
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
@@ -175,7 +177,7 @@ export const exportToExcel = (data, headers) => {
// 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");
@@ -233,4 +235,11 @@ 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";
@@ -1674,6 +1674,79 @@ const GlobalStateProvider = ({ children }) => {
},
]);
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 ]===============================
@@ -1767,6 +1840,8 @@ const GlobalStateProvider = ({ children }) => {
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

View File

@@ -1,21 +1,50 @@
import React, { useContext, useEffect, useState } from "react";
import logo from "../assets/logo2.png";
import logoDark from "../assets/logo.png";
import logoMini from "../assets/logo-min.png";
import logoMiniDark from "../assets/favicon.png";
import { useDispatch } from "react-redux";
import { loginUser } from "../Redux/Slice/auth";
import Button02 from "../Components/Buttons/Button02";
import { CgProfile } from "react-icons/cg";
import logoMiniDark from "../assets/favicon.png";
import logoMini from "../assets/logo-min.png";
import logoDark from "../assets/logo.png";
import logo from "../assets/logo2.png";
import {
TbArrowBadgeLeftFilled,
ArrowBackIcon,
ArrowLeftIcon,
ArrowRightIcon,
AtSignIcon,
} from "@chakra-ui/icons";
import {
Accordion,
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
Alert,
AlertIcon,
Box,
Button,
Image,
Text,
Tooltip,
} from "@chakra-ui/react";
import Cookies from "js-cookie"; // Import the Cookies library
import { GrManual } from "react-icons/gr";
import { HiOutlineChartSquareBar } from "react-icons/hi";
import { HiOutlineBanknotes } from "react-icons/hi2";
import { LuContact } from "react-icons/lu";
import { MdNotificationsNone, MdOutlineAddChart } from "react-icons/md";
import {
RiAccountBoxLine,
RiBankLine,
RiExchangeBoxLine,
RiFileUserLine,
RiMoneyDollarBoxLine,
} from "react-icons/ri";
import {
TbLayoutDashboard,
TbListDetails,
TbReportMoney,
TbTransactionDollar,
} from "react-icons/tb";
import { TbArrowBadgeRightFilled } from "react-icons/tb";
import { ArrowBackIcon, ArrowLeftIcon, ArrowRightIcon, AtSignIcon } from "@chakra-ui/icons";
import { VscSymbolClass } from "react-icons/vsc";
import {
Link,
NavLink,
@@ -24,70 +53,22 @@ import {
useLocation,
useNavigate,
} from "react-router-dom";
import { RouteLink } from "../Routes/Routes";
import NotFound from "../Pages/NotFound";
import { nav } from "../Routes/Nav";
import {
Avatar,
Box,
Button,
PopoverArrow,
PopoverBody,
PopoverCloseButton,
PopoverContent,
PopoverFooter,
PopoverHeader,
PopoverTrigger,
Portal,
Text,
WrapItem,
Popover,
Tag,
Accordion,
AccordionItem,
AccordionButton,
AccordionIcon,
AccordionPanel,
Image,
Alert,
AlertIcon,
Breadcrumb,
Divider,
Tooltip,
useRadio,
} from "@chakra-ui/react";
import GlobalStateContext from "../Contexts/GlobalStateContext";
import Cookies from "js-cookie"; // Import the Cookies library
import Header from "../Components/Header";
import HeaderMain from "../Components/HeaderMain";
import { IoMdSwap } from "react-icons/io";
import {
RiBankLine,
RiExchangeBoxLine,
RiFileUserLine,
RiMoneyDollarBoxLine,
} from "react-icons/ri";
import { VscSymbolClass } from "react-icons/vsc";
import { MdNotificationsNone, MdOutlineAddChart } from "react-icons/md";
import { HiOutlineChartSquareBar } from "react-icons/hi";
import { GrManual } from "react-icons/gr";
import { LuContact } from "react-icons/lu";
import shield from "../assets/shield.png";
import SplashScreen from "../Pages/SplashScreen";
import CutomBreadcrumb from "../Components/CutomBreadcrumb";
import CustomBreadcrumb from "../Components/CutomBreadcrumb";
import { getCountdownTimer } from "../Constants/Constants";
import { useLogoutMutation } from "../Services/token.serivce";
import { isMaker } from "../Constants/Constants";
import GlobalStateContext from "../Contexts/GlobalStateContext";
import CreateRequest from "../Pages/Fawateer/CreateRequest";
import ApproveRequest from "../Pages/FawateerChecker/ApproveRequest/ApproveRequest";
import ApproveHistoryMaker from "../Pages/FawateerChecker/ApproveHistory/ApproveHistoryMaker";
import ApproveHistory from "../Pages/FawateerChecker/ApproveHistory/ApproveHistoryChecker";
import ApproveHistoryMaker from "../Pages/FawateerChecker/ApproveHistory/ApproveHistoryMaker";
import ApproveRequest from "../Pages/FawateerChecker/ApproveRequest/ApproveRequest";
import NotFound from "../Pages/NotFound";
import SplashScreen from "../Pages/SplashScreen";
import { nav } from "../Routes/Nav";
import { RouteLink } from "../Routes/Routes";
import { useProfileQuery } from "../Services/io.service";
import { useLogoutMutation } from "../Services/token.serivce";
const DashboardLayout = ({ isOnline }) => {
const userRole = localStorage.getItem("role");
const navigate = useNavigate();
const dispach = useDispatch();
const location = useLocation();
const path = location.pathname;
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
@@ -104,15 +85,15 @@ const DashboardLayout = ({ isOnline }) => {
const { data, refetch } = useProfileQuery();
useEffect(() => {
if (
!localStorage.getItem("accessToken") &&
!localStorage.getItem("refreshToken")
) {
logOutHandler();
return navigate("/login");
}
}, []);
// useEffect(() => {
// if (
// !localStorage.getItem("accessToken") &&
// !localStorage.getItem("refreshToken")
// ) {
// logOutHandler();
// return navigate("/login");
// }
// }, []);
useEffect(() => {
const savedIndex = localStorage.getItem("openAccordionIndex");
@@ -151,7 +132,9 @@ const DashboardLayout = ({ isOnline }) => {
await logout();
localStorage.clear();
navigate("/login");
} catch (error) {}
} catch (error) {
console.log(error);
}
};
// // Function to get the title based on the route
@@ -159,35 +142,50 @@ const DashboardLayout = ({ isOnline }) => {
switch (true) {
case "/":
return "👋🏻 Hi, Admin";
// case path.startsWith("/"):
// return (z
// <span className="d-flex align-items-end gap-2">
// <TbLayoutDashboard className="h4 m-0" />Dashboard
// </span>
// );
case path.startsWith("/investment-opportunities"):
return (
<span className="d-flex align-items-end gap-2">
<TbLayoutDashboard className="h4 m-0" />
dashboard / Open Opportunities
</span>
);
case path.startsWith("/sponser"):
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0" /> Sponsor
</span>
);
case path.startsWith("/email"):
return (
<span className="d-flex align-items-end gap-2">
<AtSignIcon className="h4 m-0" /> Email Notifiation
</span>
);
case path.startsWith("/email"):
return (
<span className="d-flex align-items-end gap-2">
<AtSignIcon className="h4 m-0" /> Email Notification
</span>
);
case path.startsWith("/investment-type"):
return (
<span className="d-flex align-items-end gap-2">
<VscSymbolClass className="h4 m-0" /> Investment Type
</span>
);
case path.startsWith("/profile"):
return (
<span className="d-flex align-items-end gap-2">
<CgProfile className="h4 m-0" /> Profile
</span>
);
case path.startsWith("/profile"):
return (
<span className="d-flex align-items-end gap-2">
<CgProfile className="h4 m-0" /> Profile
</span>
);
case path.startsWith("/exchange-rate"):
return (
<span className="d-flex align-items-end gap-2">
<RiExchangeBoxLine className="h4 m-0 fw-normal" />
Echange rate
Exchange rate
</span>
);
case path.startsWith("/create-io"):
@@ -238,10 +236,10 @@ const DashboardLayout = ({ isOnline }) => {
return (
<span className="d-flex align-items-end gap-2">
<RiExchangeBoxLine className="h4 m-0 fw-normal" />
Deposite Request
Deposit Request
</span>
);
case path.startsWith("/fawateer"):
return (
<span className="d-flex align-items-end gap-2">
@@ -249,33 +247,33 @@ const DashboardLayout = ({ isOnline }) => {
Fawateer Deposit
</span>
);
case path.startsWith("/fawateer-history"):
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
Fawateer Deposit
</span>
);
case path.startsWith("/fawateer-history"):
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
Fawateer Deposit
</span>
);
case path.startsWith("/withdraw-request"):
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
Withdrawal pending request
Withdrawal Pending Request
</span>
);
case path.startsWith("/withdraw-history"):
return (
<span className="d-flex align-items-end gap-2">
<RiExchangeBoxLine className="h4 m-0 fw-normal" />
Withdrawal request
Withdrawal Request
</span>
);
case path.startsWith("/investor-request"):
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
Investor pending request
Investor Pending Request
</span>
);
case path.startsWith("/investor-history"):
@@ -289,16 +287,39 @@ const DashboardLayout = ({ isOnline }) => {
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
Deletion pending request
Deletion Pending Request
</span>
);
case path.startsWith("/deletion-history"):
return (
<span className="d-flex align-items-end gap-2">
<RiExchangeBoxLine className="h4 m-0 fw-normal" />
Deletion request
Deletion Request
</span>
);
case path.startsWith("/bank-deposit-request"):
return (
<span className="d-flex align-items-end gap-2">
<RiBankLine className="h4 m-0 fw-normal" />
Reversal Transaction / Deposit Request
</span>
);
case path.startsWith("/reversal-fawateer-deposit"):
return (
<span className="d-flex align-items-end gap-2">
<HiOutlineBanknotes className="h4 m-0 fw-normal" />
Reversal Transaction / Fawateer Deposit
</span>
);
case path.startsWith("/account-deletion-request"):
return (
<span className="d-flex align-items-end gap-2">
<RiAccountBoxLine className="h4 m-0 fw-normal" />
Reversal Transaction / Account Deletion Request
</span>
);
case path.startsWith("/bank-investor"):
return (
<span className="d-flex align-items-end gap-2">
@@ -317,7 +338,7 @@ const DashboardLayout = ({ isOnline }) => {
return (
<span className="d-flex align-items-end gap-2">
<MdNotificationsNone className="h4 m-0 fw-normal" />
Notification
Push Notification
</span>
);
case path.startsWith("/contact"):
@@ -345,7 +366,7 @@ const DashboardLayout = ({ isOnline }) => {
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
Deletion pending request
Deletion Pending Request
</span>
);
case path.startsWith("/deletion-history"):
@@ -359,7 +380,7 @@ const DashboardLayout = ({ isOnline }) => {
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
Deletion pending request
Deletion Pending Request
</span>
);
case path.startsWith("/deletion-history"):
@@ -369,6 +390,13 @@ const DashboardLayout = ({ isOnline }) => {
Deletion request
</span>
);
case path.startsWith("/subadmin"):
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
Manage SubAdmin
</span>
);
default:
if (path.startsWith("/community/view/")) {
@@ -398,6 +426,32 @@ const DashboardLayout = ({ isOnline }) => {
return <SplashScreen />;
}
const _filteredNav = isMaker()
? nav.filter(
(item) =>
item.title !== "REVERSAL TRANSACTION" &&
item.path !== "/bank-deposit-request" &&
item.path !== "/reversal-fawateer-deposit" &&
item.path !== "/account-deletion-request"
)
: nav;
const filteredNav = _filteredNav.map((item) => {
if (item.submenu) {
return {
...item,
submenu: item.submenu.filter(
(submenuItem) =>
!(!data?.data?.superAdmin && submenuItem.title === "Sub Admin")
),
};
}
// if (item.title === "REVERSAL TRANSACTION" && item.type === "title") {
// }
return item;
});
return (
<Box
style={{
@@ -515,10 +569,169 @@ const DashboardLayout = ({ isOnline }) => {
index={openIndex}
onChange={handleAccordionChange}
>
{nav.map(({ title, type, Icon, submenu, path }, index) => {
if (type === "accordion") {
return (
<AccordionItem key={index} border={"none"}>
{filteredNav.map(
({ title, type, Icon, submenu, path }, index) => {
if (type === "accordion") {
return (
<AccordionItem key={index} border={"none"}>
<Tooltip
isDisabled={isDrawerOpen || openDrawerClick}
hasArrow
bg={"#fff"}
fontSize={"xs"}
label={title}
placement="top-start"
color={"blue.800"}
>
<AccordionButton
style={{ height: "auto" }}
className={`${
isDrawerOpen || openDrawerClick
? "p-2 web-text-medium ps-3 justify-content-between"
: "p-2 ps-1 web-text-xlarge justify-content-center"
} rounded-1 link d-flex align-items-center gap-2 w-100 mb-1`}
>
<Box
as="span"
display={"flex"}
gap={2}
alignItems={"center"}
>
{/* {Icon && title === "Admin" ? <Image w={15} src={shield} /> : <Icon className={`web-text-large`} />} */}
{Icon && (
<Icon
fontSize={title === "Admin" ? "18px" : "15px"}
/>
)}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick
? "flex"
: "none"
}
alignItems="center"
overflow="hidden"
textAlign={"left"}
>
{title}
</Text>
</Box>
<AccordionIcon />
</AccordionButton>
</Tooltip>
<AccordionPanel
p={0}
pb={1}
display={"flex"}
flexDirection={"column"}
gap={1}
>
{submenu?.map(
(
{
title: subMenuTitle,
path: link,
icon: SubIcon,
},
i
) => (
<Tooltip
isDisabled={isDrawerOpen || openDrawerClick}
hasArrow
bg={"#fff"}
fontSize={"xs"}
label={subMenuTitle}
placement="right"
color={"blue.800"}
>
<Box
key={i}
style={{
height: "auto",
position: "relative",
}}
className={`${
isDrawerOpen || openDrawerClick
? " web-text-medium ps-4"
: " web-text-xlarge justify-content-center"
} d-flex align-items-center p-0`}
>
<Box
backgroundColor={"gray.300"}
style={{
position: "absolute",
top: 0,
width: 2,
left: 22,
height:
i === submenu?.length - 1
? "55%"
: "120%",
borderRadius: "0 0 10px 10px",
}}
/>
<Box
backgroundColor={"gray.300"}
style={{
position: "absolute",
width: 10,
left: 22,
height: 2,
}}
/>
<NavLink
className={`${
isDrawerOpen || openDrawerClick
? "p-2 ps-1 ms-2 web-text-medium "
: "p-2 ps-0 ms-0 zindex-3 ms-4 web-text-xlarge justify-content-center"
} rounded-1 link d-flex align-items-center gap-2 w-100 `}
to={link}
>
{SubIcon && (
<SubIcon
className="web-text-large ms-2"
style={{ zIndex: 111 }}
/>
)}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick
? "flex"
: "none"
}
alignItems="center"
overflow="hidden"
>
{subMenuTitle === "Aprover Request"
? data?.data?.role === "Maker"
? "Create Request"
: "Aprover Request"
: subMenuTitle}
</Text>
</NavLink>
</Box>
</Tooltip>
)
)}
</AccordionPanel>
</AccordionItem>
);
} else if (type === "title") {
return (
<Text
as={"span"}
key={index}
className="web-text-xxsmall fw-600 text-secondary fw-bold"
>
{title}
</Text>
);
} else if (type === "single") {
return (
<Tooltip
isDisabled={isDrawerOpen || openDrawerClick}
hasArrow
@@ -528,184 +741,35 @@ const DashboardLayout = ({ isOnline }) => {
placement="top-start"
color={"blue.800"}
>
<AccordionButton
style={{ height: "auto" }}
<NavLink
key={index}
style={{ height: "auto", position: "relative" }}
className={`${
isDrawerOpen || openDrawerClick
? "p-2 web-text-medium ps-3 justify-content-between"
: "p-2 ps-1 web-text-xlarge justify-content-center"
} rounded-1 link d-flex align-items-center gap-2 w-100 mb-1`}
? "p-2 web-text-medium"
: "p-2 ps-0 web-text-xlarge justify-content-start"
} rounded-1 link d-flex align-items-center gap-2 w-100 mb-2`}
to={path}
>
<Box
as="span"
display={"flex"}
gap={2}
alignItems={"center"}
{Icon && <Icon className="web-text-large ms-2" />}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick ? "flex" : "none"
}
alignItems="center"
overflow="hidden"
>
{/* {Icon && title === "Admin" ? <Image w={15} src={shield} /> : <Icon className={`web-text-large`} />} */}
{Icon && (
<Icon
fontSize={title === "Admin" ? "18px" : "15px"}
/>
)}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick
? "flex"
: "none"
}
alignItems="center"
overflow="hidden"
textAlign={"left"}
>
{title}
</Text>
</Box>
<AccordionIcon />
</AccordionButton>
{title}
</Text>
</NavLink>
</Tooltip>
<AccordionPanel
p={0}
pb={1}
display={"flex"}
flexDirection={"column"}
gap={1}
>
{submenu?.map(
(
{ title: subMenuTitle, path: link, icon: SubIcon },
i
) => (
<Tooltip
isDisabled={isDrawerOpen || openDrawerClick}
hasArrow
bg={"#fff"}
fontSize={"xs"}
label={subMenuTitle}
placement="right"
color={"blue.800"}
>
<Box
key={i}
style={{ height: "auto", position: "relative" }}
className={`${
isDrawerOpen || openDrawerClick
? " web-text-medium ps-4"
: " web-text-xlarge justify-content-center"
} d-flex align-items-center p-0`}
>
<Box
backgroundColor={"gray.300"}
style={{
position: "absolute",
top: 0,
width: 2,
left: 22,
height:
i === submenu?.length - 1
? "55%"
: "120%",
borderRadius: "0 0 10px 10px",
}}
/>
<Box
backgroundColor={"gray.300"}
style={{
position: "absolute",
width: 10,
left: 22,
height: 2,
}}
/>
<NavLink
className={`${
isDrawerOpen || openDrawerClick
? "p-2 ps-1 ms-2 web-text-medium "
: "p-2 ps-0 ms-0 zindex-3 ms-4 web-text-xlarge justify-content-center"
} rounded-1 link d-flex align-items-center gap-2 w-100 `}
to={link}
>
{SubIcon && (
<SubIcon
className="web-text-large ms-2"
style={{ zIndex: 111 }}
/>
)}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick
? "flex"
: "none"
}
alignItems="center"
overflow="hidden"
>
{subMenuTitle === "Aprover Request"
? data?.data?.role === "Maker"
? "Create Request"
: "Aprover Request"
: subMenuTitle}
</Text>
</NavLink>
</Box>
</Tooltip>
)
)}
</AccordionPanel>
</AccordionItem>
);
} else if (type === "title") {
return (
<Text
as={"span"}
key={index}
className="web-text-xxsmall fw-600 text-secondary fw-bold"
>
{title}
</Text>
);
} else if (type === "single") {
return (
<Tooltip
hasArrow
bg={"#fff"}
fontSize={"xs"}
label={title}
placement="top-start"
color={"blue.800"}
>
<NavLink
key={index}
style={{ height: "auto", position: "relative" }}
className={`${
isDrawerOpen || openDrawerClick
? "p-2 web-text-medium"
: "p-2 ps-0 web-text-xlarge justify-content-start"
} rounded-1 link d-flex align-items-center gap-2 w-100`}
to={path}
>
{Icon && <Icon className="web-text-large ms-2" />}
<Text
as={"span"}
display={
isDrawerOpen || openDrawerClick ? "flex" : "none"
}
alignItems="center"
overflow="hidden"
>
{title}
</Text>
</NavLink>
</Tooltip>
);
} else {
return null;
);
} else {
return null;
}
}
})}
)}
</Accordion>
</Box>
@@ -788,6 +852,7 @@ const AppContent = ({ data }) => {
)
}
/>
<Route path="*" element={<NotFound />} />
</Routes>
);

View File

@@ -7,6 +7,8 @@ import {
Input,
Text,
Tooltip,
useBoolean,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
@@ -19,25 +21,37 @@ 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, setDeleteHistory } =
useContext(GlobalStateContext);
const { slideFromRight, setDeleteHistory } = useContext(GlobalStateContext);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
// =========================== [Use State] =============================
// =========================== [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();
const [createAccountDeletionReversalRequest] =
useCreateAccountDeletionReversalRequestMutation();
// Debounce the search term to avoid making a request on every keystroke
useEffect(() => {
const handler = setTimeout(() => {
@@ -48,33 +62,33 @@ const DeletionHistory = () => {
};
}, [searchTerm]);
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
})
refetch,
} = useGetDeleteHistoryQuery(
{
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
search: debouncedSearchTerm,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
}
);
const formatDate = (date) => {
return new Date(date).toLocaleDateString('en-GB', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
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]);
// Use useEffect to refetch data when the component mounts
useEffect(() => {
refetch();
}, [refetch]);
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
@@ -85,7 +99,8 @@ const DeletionHistory = () => {
"Last Name",
"Country",
"Phone Number",
"Status"
"Status",
isMaker() && "Reversal Action",
];
const extractedArray = deleteHistory?.data?.rows?.map((item, index) => ({
@@ -96,7 +111,7 @@ const DeletionHistory = () => {
as={"span"}
color={"gray.800"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
fontWeight={"500"}
>
{index + 1}.
</Text>
@@ -107,7 +122,7 @@ const DeletionHistory = () => {
as={"span"}
color={"gray.600"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
fontWeight={"500"}
>
{formatDate(item.Requested_on)}
</Text>
@@ -118,7 +133,7 @@ const DeletionHistory = () => {
as={"span"}
color={"gray.600"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
fontWeight={"500"}
>
{item.clientId}
</Text>
@@ -129,7 +144,7 @@ const DeletionHistory = () => {
as={"span"}
color={"gray.800"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
fontWeight={"500"}
>
{item.firstName}
{/* {formatDate(item.charge)} */}
@@ -141,51 +156,77 @@ const DeletionHistory = () => {
as={"span"}
color={"gray.800"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
fontWeight={"500"}
>
{item.lastName}
</Text>
),
"Country": (
<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.country}
</Text>
),
"Phone Number": (
<Text
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center web-text-small"
fontWeight={'500'}
fontWeight={"500"}
>
{item.phoneNumber}
</Text>
),
"Status": (
<Text
Status: (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={item?.deletionStatus? "red.500": "blue.500"}
color={item?.deletionStatus === "Approved" ? "red.500" : "blue.500"}
className="d-flex align-items-center web-text-small"
fontWeight={'600'}
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);
@@ -195,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">
@@ -244,6 +324,12 @@ const DeletionHistory = () => {
alertHandler={handleDelete}
isLoading={isLoading}
/>
<InitiateReversalPopup
onClose={onCloseInRev}
isOpen={isOpenInRev}
handelApproved={handleApproved}
isLoading={isReversalLoading}
/>
</Box>
);
};

View File

@@ -91,17 +91,17 @@ const DeletionRequest = () => {
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr No.",
"Requested on",
"Requested On",
"Client ID",
"First name",
"Last name",
"First Name",
"Last Name",
"Country",
"Phone number",
"Phone Number",
"Status",
"Action"
];
const extractedArray = filteredData?.map((item, index) => ({
const extractedArray = data?.data?.rows?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Text
@@ -114,7 +114,7 @@ const DeletionRequest = () => {
{index + 1}.
</Text>
),
"Requested on": (
"Requested On": (
<Text
justifyContent={"left"}
as={"span"}
@@ -136,7 +136,7 @@ const DeletionRequest = () => {
{item?.clientId}
</Text>
),
"First name": (
"First Name": (
<Text
justifyContent={"left"}
as={"span"}
@@ -148,7 +148,7 @@ const DeletionRequest = () => {
{/* {formatDate(item.charge)} */}
</Text>
),
"Last name": (
"Last Name": (
<Text
justifyContent={"left"}
as={"span"}
@@ -171,7 +171,7 @@ const DeletionRequest = () => {
</Text>
),
"Phone number": (
"Phone Number": (
<Text
justifyContent={"left"}
as={"span"}

View File

@@ -3,6 +3,7 @@ import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
@@ -21,88 +22,99 @@ 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 {
useGetDepositRequestByIdQuery,
useGetDepositRequestQuery,
useUpdateDepositRequestMutation,
} from "../../Services/deposit.request.service";
import FullscreenLoaders from "../../Components/Loaders/FullscreenLoaders";
import ToastBox from "../../Components/ToastBox";
import { useGetDrawalRequestQuery } from "../../Services/drawal.request.service";
import { useApproveDepositRequestMutation, useGetDeleteRequestByIdQuery } from "../../Services/delete.request.service";
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 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 [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, {
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)
comment: fileredData?.comment,
});
}, [requestData, id]);
const onSubmit = async (data) => {
setIsBtnLoading(isReject ? false : true);
setIsBtnLoadingReject(isReject);
const approveReq = {
adminComment:data?.adminComment,
deletionStatus: isReject?"Reject": "Approved"
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);
}
try {
const res = await updateApproveRequest({ id ,data:approveReq })
if (res?.error) {
toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} />
),
});
heandleOnClose()
}else if(res?.data?.statusCode === 200) {
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
});
heandleOnClose()
}
} catch (error) {
}
};
const onReject = () => {
}
const onReject = () => {};
useEffect(() => {
if (data) {
@@ -112,16 +124,20 @@ const DeletionRequestApprove = ({ isOpen, onClose, firstField, id, data:requestD
}
}, [data, reset]);
const heandleOnClose = () =>{
reset()
onClose()
setIsBtnLoading(false)
setIsReject(false)
setIsBtnLoadingReject(false)
}
const heandleOnClose = () => {
reset();
onClose();
setIsBtnLoading(false);
setIsReject(false);
setIsBtnLoadingReject(false);
};
return (
<Modal isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<Modal
isOpen={isOpen}
onClose={heandleOnClose}
initialFocusRef={firstField}
>
<ModalOverlay />
<ModalContent pb={4}>
@@ -133,7 +149,10 @@ const DeletionRequestApprove = ({ isOpen, onClose, firstField, id, data:requestD
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<FormControl mt={6} mb={4}>
<FormLabel fontSize="sm">Investor Comment <Badge colorScheme="green">{fileredData?.currencyCode}</Badge></FormLabel>
<FormLabel fontSize="sm">
Investor Comment{" "}
<Badge colorScheme="green">{fileredData?.currencyCode}</Badge>
</FormLabel>
{/* <Textarea
focusBorderColor="green.400"
name="comment"
@@ -150,8 +169,9 @@ const DeletionRequestApprove = ({ isOpen, onClose, firstField, id, data:requestD
</Text>
)} */}
<Text fontSize="sm" fontWeight={500} color={'gray.600'}>{data?.data?.comment}</Text>
<Text fontSize="sm" fontWeight={500} color={"gray.600"}>
{data?.data?.comment}
</Text>
</FormControl>
<FormControl mb={4} isRequired>
<FormLabel fontSize="sm">Admin Comment</FormLabel>
@@ -163,14 +183,22 @@ const DeletionRequestApprove = ({ isOpen, onClose, firstField, id, data:requestD
fontSize="sm"
type="textarea"
size="sm"
placeholder={"Enter your comments...."}
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>
@@ -180,9 +208,10 @@ const DeletionRequestApprove = ({ isOpen, onClose, firstField, id, data:requestD
type="submit"
size={"sm"}
rounded={"sm"}
variant={'ghost'}
onClick={()=> setIsReject(true)}
variant={"ghost"}
onClick={() => setIsReject(true)}
isLoading={isBtnLoadingReject}
fontWeight={500}
>
Reject
</Button>

View File

@@ -1,27 +1,25 @@
import {
Avatar,
Badge,
Box,
HStack,
Input,
Select,
Switch,
Text,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState, useRef } from "react";
import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import { debounce } from "../../../Master/Sponser/AddSponser";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import React, { useContext, useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import Pagination from "../../../../Components/Pagination";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import ToastBox from "../../../../Components/ToastBox";
import ReasonBanModal from "./ReasonBanModal";
import { useGetbanInvestorQuery } from "../../../../Services/ban.investor.service";
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
@@ -233,8 +231,6 @@ const BankInvestor = () => {
),
}));
console.log(extractedArray);
const handleDelete = () => {
const updatedInvestorDetails = InvestorDetails.filter(
(sponsor) => sponsor.id !== actionId
@@ -278,6 +274,14 @@ const BankInvestor = () => {
/>
<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"}

View File

@@ -34,7 +34,7 @@ import { useUpdateBanMutation, useUpdateUnbanMutation } from "../../../../Servic
const toast = useToast()
const {
register,
register,
reset,
handleSubmit,
formState: { errors },

View File

@@ -1,31 +1,26 @@
import {
Avatar,
Badge,
Box,
HStack,
Input,
Select,
Switch,
Text,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState, useRef } from "react";
import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import { debounce } from "../../../Master/Sponser/AddSponser";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import DataTable from "../../../../Components/DataTable/NormalTable";
import React, { useContext, useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import Pagination from "../../../../Components/Pagination";
import DataTable from "../../../../Components/DataTable/NormalTable";
import ToastBox from "../../../../Components/ToastBox";
import ReasonBanModal from "./ReasonBanModal";
import {
useGetInvestorQuery,
useGetUnbanInvestorQuery,
} from "../../../../Services/ban.investor.service";
import { generateSerialNumber } from "../../../../Constants/Constants";
import { TABLE_PAGINATION } from "../../../../Constants/Paginations";
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
@@ -270,6 +265,14 @@ const UnbanInvestor = () => {
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"}

View File

@@ -3,11 +3,14 @@ import {
Badge,
Box,
Button,
HStack,
Input,
Select,
Text,
Tooltip,
useToast,
} from "@chakra-ui/react";
import { useForm} from "react-hook-form";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import { useNavigate } from "react-router-dom";
@@ -23,15 +26,16 @@ import ToastBox from "../../Components/ToastBox";
import NormalTable from "../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../Contexts/GlobalStateContext";
import { useGetInvestorsQuery } from "../../Services/investor.details.service";
import { TABLE_PAGINATION } from "../../Constants/Paginations";
import {
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 const notification = yup.object().shape({
title: yup
.string()
.required("Investment Name is required"),
title: yup.string().required("Notification Header is required"),
ManualDate: yup
.date()
.required("Manual Date is required")
@@ -43,33 +47,28 @@ export const notification = yup.object().shape({
/^([01]\d|2[0-3]):?([0-5]\d)$/,
"Invalid time format, must be in HH:mm"
),
expectedReturn: yup
.string()
.required("Expected Return is required"),
expectedReturn: yup.string().required("Expected Return is required"),
});
export const notificationNew = yup.object().shape({
title: yup
.string()
.required("Investment Name is required"),
message: yup
.string()
.required("Message is required"),
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(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
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,
@@ -80,21 +79,20 @@ const Notification = () => {
} = useForm({
resolver: yupResolver(notificationNew),
defaultValues: {
title: '',
message: '',
},
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",
@@ -109,28 +107,40 @@ const Notification = () => {
// // 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,
data: investorDetails,
isLoading: investorDetailsLoading,
refetch,
} = useGetUnbanInvestorQuery({
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
search: debouncedSearchTerm,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
});;
console.log(investorDetails);
} = 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 />;
}
@@ -141,9 +151,11 @@ const Notification = () => {
placeHolder: " ",
name: "title",
type: "text",
width:"100%",
maxLength:100,
helperText:`Maximum length should be 100 characters. You have entered ${watch()?.title?.length || 0} characters.`,
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 || "",
@@ -152,15 +164,16 @@ const Notification = () => {
label: "Notification Message",
placeHolder: " ",
name: "message",
width:"100%",
width: "100%",
type: "textarea",
isRequired: true,
maxLength:200,
helperText:`Maximum length should be 200 characters. You have entered ${watch()?.message?.length || 0} characters.`,
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) => {
@@ -173,55 +186,46 @@ const Notification = () => {
}, {});
const onSubmit = async (data) => {
const dataToPass = {
...data,
principal_xid:selectedRadio
}
principal_xid: selectedRadio,
};
setIsLoading(true);
try {
const res = await sendNotification(dataToPass);
console.log(res);
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsLoading(false)
}else if(res?.data){
setIsLoading(false);
} else if (res?.data) {
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
render: () => <ToastBox message={res?.data?.message} />,
});
setIsLoading(false)
setSelectedRadio([])
setIsLoading(false);
setSelectedRadio([]);
reset({
title: '', // Resetting specific fields
message: '',
title: "", // Resetting specific fields
message: "",
}); // Clears the form fields
}else{
} else {
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
<ToastBox status={"error"} message={"Something went wrong"} />
),
});
setIsLoading(false)
setIsLoading(false);
}
} catch (error) {
console.log(error);
setIsLoading(false);
}
};
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr N/O",
@@ -235,7 +239,6 @@ const Notification = () => {
"KYC Status",
];
const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({
id: item?.principal_xid,
"Sr N/O": (
@@ -245,7 +248,7 @@ const Notification = () => {
color={"gray.600"}
className="d-flex align-items-center fw-bold web-text-small"
>
{generateSerialNumber(idx,currentPage, pageSize )}
{generateSerialNumber(idx, currentPage, pageSize)}
</Text>
),
Date: (
@@ -305,9 +308,9 @@ const Notification = () => {
color={item?.KYCStatus === false ? "red" : "blue"}
px={2}
py={0.5}
variant={'ghost'}
variant={"ghost"}
>
{item?.KYCStatus === true ? "Completed" : "Incompleted"}
{item?.KYCStatus === true ? "Completed" : "Not Completed"}
</Badge>
</Box>
),
@@ -322,18 +325,77 @@ const Notification = () => {
onSubmit={handleSubmit(onSubmit)}
btnLoading={isLoading}
>
<Box overflow={'scroll'} h={'58vh'}>
<NormalTable
centered={true}
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
// isLoading={isLoading}
setSelectedRadio={setSelectedRadio}
selectedRadio={selectedRadio}
showRadioButton={true}
/>
</Box>
<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>
);

View File

@@ -0,0 +1,463 @@
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",
"Deposit Amount",
"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 isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.countryName}
</Text>
</Box>
),
"Phone Number": (
<Box isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.ISDCode} {item?.mobileNumber}
{/* {item?.ISDcode + " " + 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>
),
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

@@ -4,39 +4,34 @@ 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 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";
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 { generateSerialNumber } from "../../../Constants/Constants";
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',
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
}; // Simple date formatter
@@ -50,22 +45,22 @@ const DepositHistory = () => {
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 [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(() => {
@@ -77,20 +72,21 @@ const DepositHistory = () => {
};
}, [searchTerm]);
const {
data,
error,
refetch,
isLoading: depositHistoryLoading,
} = useGetDepositHistoryQuery({
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
search: debouncedSearchTerm,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
});
} = 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(() => {
@@ -109,6 +105,7 @@ const DepositHistory = () => {
"Deposit Date",
"Status",
"Supporting's",
isMaker() && "Reversal Action",
];
const handleUpdateStatus = debounce((id) => {
@@ -161,7 +158,7 @@ const DepositHistory = () => {
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{generateSerialNumber(idx,currentPage, pageSize )}
{generateSerialNumber(idx, currentPage, pageSize)}
</Text>
),
"Client ID": (
@@ -205,11 +202,7 @@ const DepositHistory = () => {
</Box>
),
"Deposit Amount": (
<Box
isTruncated={true}
display={"flex"}
justifyContent={"end"}
>
<Box isTruncated={true} display={"flex"} justifyContent={"end"}>
<Text as={"span"} color={"teal.900"} textAlign={"right"}>
{parseFloat(item?.investorAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
@@ -283,31 +276,91 @@ const DepositHistory = () => {
) : (
""
),
"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"}
@@ -354,12 +407,19 @@ const DepositHistory = () => {
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

@@ -7,10 +7,11 @@ import {
FormLabel,
HStack,
Input,
Select,
Text,
useToast,
} from "@chakra-ui/react";
import React, { useState } from "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";
@@ -28,8 +29,11 @@ const EmailNotification = () => {
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 = [
@@ -47,14 +51,40 @@ const EmailNotification = () => {
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: currentPage, // Omit pagination for search
size: 10000, // Omit pagination for search
});
} = 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,
@@ -127,7 +157,7 @@ const EmailNotification = () => {
py={0.5}
variant={"ghost"}
>
{item?.KYCStatus === true ? "Completed" : "Incompleted"}
{item?.KYCStatus === true ? "Completed" : "Not Completed"}
</Badge>
</Box>
),
@@ -135,9 +165,9 @@ const EmailNotification = () => {
const modules = {
toolbar: [
// [{ header: "1" }, { header: "2" },
// // { font: [] }
// ],
// [{ header: "1" }, { header: "2" },
// // { font: [] }
// ],
// [{ size: [] }],
["bold", "italic", "underline", "strike", "blockquote"],
[{ list: "ordered" }, { list: "bullet" }],
@@ -147,12 +177,15 @@ const EmailNotification = () => {
// Main submission logic
const handleSend = async (e) => {
e.preventDefault(); // Prevent default form submission
e.preventDefault(); // Prevent default form submission
if (!subject || !value) {
toast({
render: () => (
<ToastBox status={"error"} message={"Subject or email body cannot be empty"} />
<ToastBox
status={"error"}
message={"Subject or email body cannot be empty"}
/>
),
});
return;
@@ -161,7 +194,10 @@ const EmailNotification = () => {
if (selectedRadio.length === 0) {
toast({
render: () => (
<ToastBox status={"error"} message={"Please select at least one recipient"} />
<ToastBox
status={"error"}
message={"Please select at least one recipient"}
/>
),
});
return;
@@ -172,44 +208,36 @@ const EmailNotification = () => {
const emailPayload = {
subject,
body: value,
principal_xid: selectedRadio,
principal_xid: selectedRadio,
};
try {
const res = await sendCustomNotification(emailPayload)
console.log(res);
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){
setIsLoading(false);
} else if (res?.data) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
setIsLoading(false);
setSubject("");
setValue("");
setSelectedRadio([]);
} else {
toast({
render: () => (
<ToastBox message={res?.data?.message} />
<ToastBox status={"error"} message={"Something went wrong"} />
),
});
setIsLoading(false)
setSubject("")
setValue("")
setSelectedRadio([])
}else{
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
),
});
setIsLoading(false)
setIsLoading(false);
}
} catch (error) {
}
} catch (error) {}
};
return (
@@ -247,40 +275,92 @@ const EmailNotification = () => {
{/* <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..."
/>
<ReactQuill
theme="snow"
style={{
height: 300,
}}
value={value}
onChange={setValue}
modules={modules}
placeholder="Start typing here..."
/>
</FormControl>
{/* <FormHelperText fontSize={"xs"}>
We'll never share your email.
</FormHelperText> */}
</FormControl>
<Box overflow={'scroll'} h={'58vh'}>
<NormalTable
centered={true}
emptyMessage={`We don't have any Sponsors`}
tableHeadRow={tableHeadRow}
data={extractedArray}
setSelectedRadio={setSelectedRadio}
selectedRadio={selectedRadio}
showRadioButton={true}
/>
<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}>

View File

@@ -1,46 +1,25 @@
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 { OPACITY_ON_LOAD } from "../../Layout/animations";
import NormalTable from "../../Components/DataTable/NormalTable";
import { HiDotsVertical } from "react-icons/hi";
import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
import {
AddIcon,
DeleteIcon,
EditIcon,
EmailIcon,
ViewIcon,
} from "@chakra-ui/icons";
import Pagination from "../../Components/Pagination";
import GlobalStateContext from "../../Contexts/GlobalStateContext";
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 { useGetInvestorsQuery } from "../../Services/investor.details.service";
import { generateSerialNumber } from "../../Constants/Constants";
import { TABLE_PAGINATION } from "../../Constants/Paginations";
import { exportToExcel, generateSerialNumber } from "../../Constants/Constants";
import { LuFileSpreadsheet } from "react-icons/lu";
import { useGetInvestorsQuery } from "../../Services/investor.details.service";
const FawateerRequest = () => {
const navigate = useNavigate();
@@ -60,8 +39,7 @@ const FawateerRequest = () => {
} = useDisclosure();
const btnRef = React.useRef();
// =========================== [Use State] =============================
// =========================== [Use State] =============================
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const [searchTerm, setSearchTerm] = useState("");
@@ -77,21 +55,20 @@ const FawateerRequest = () => {
};
}, [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
}
);
} = 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
@@ -109,10 +86,10 @@ const FawateerRequest = () => {
"Client ID",
"First Name",
"Last Name",
"Country",
"Country",
"Phone Number",
"E-mail ID",
// "Type",
// "Type",
// "KYC Status",
"Approval Status",
];
@@ -131,7 +108,15 @@ const FawateerRequest = () => {
// ====================================================[Table Filter]================================================================
const filteredData = investorDetails?.data?.rows?.filter((item) => {
// Filter by name (case insensitive)
const name = [item?.principal?.firstName, item?.principal?.lastName, item?.country?.countryName, item?.principal?.mobileNumber, item?.principal?.emailAddress].filter(Boolean).join(' ');
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);
@@ -147,35 +132,31 @@ const FawateerRequest = () => {
return nameMatches;
});
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: "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: "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
{ 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 No": (
<Text
w={'24px'}
w={"24px"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center fw-bold web-text-small"
>
{/* {item.id} */}
{generateSerialNumber(idx,currentPage, pageSize )}
{generateSerialNumber(idx, currentPage, pageSize)}
</Text>
),
"Client ID": (
@@ -183,7 +164,7 @@ const FawateerRequest = () => {
<Text as={"span"} color={"teal.900"}>
{item.clientReference_id}
</Text>
</Box>
</Box>
),
"First Name": (
<Box w={"auto"} isTruncated={true}>
@@ -220,27 +201,33 @@ const FawateerRequest = () => {
</Text>
</Box>
),
"Type": (
Type: (
<Box w={"auto"} isTruncated={true}>
<Text as={"span"} >
<Badge color={"forestGreen.500"} variant={'ghost'} fontWeight={"700"} px={2} py={0.5}>
<Text as={"span"}>
<Badge
color={"forestGreen.500"}
variant={"ghost"}
fontWeight={"700"}
px={2}
py={0.5}
>
{item?.investor_type?.investorTypeName}
</Badge>
</Text>
</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>
<Box w={"auto"} isTruncated={true}>
<Badge
fontWeight={"700"}
textTransform={"none"}
colorScheme={item.ioStatus ? "red" : "purple"}
px={2}
py={0.5}
>
Approved
</Badge>
</Box>
),
}));
@@ -264,8 +251,6 @@ const FawateerRequest = () => {
console.log(investorDetails?.data?.totalItems);
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<Box bg="white.500">
@@ -290,23 +275,17 @@ const FawateerRequest = () => {
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
<Pagination
isLoading={investorDetailsLoading}
isLoading={investorDetailsLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={investorDetails?.data?.totalItems}
/>
{/*
{/*
<Button
leftIcon={<AddIcon />}
colorScheme="forestGreen"
@@ -317,7 +296,6 @@ const FawateerRequest = () => {
>
Create request
</Button> */}
</HStack>
</HStack>
</Box>
@@ -345,4 +323,4 @@ const FawateerRequest = () => {
);
};
export default FawateerRequest
export default FawateerRequest;

View File

@@ -37,6 +37,7 @@ const ApproveHistory = () => {
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);
@@ -60,12 +61,31 @@ const ApproveHistory = () => {
onClose: onRejectClose,
} = useDisclosure();
// 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: drawalRequestLoading,
error,
refetch,
} = useGetApproveHistoryQuery();
} = useGetApproveHistoryQuery(
{
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);
@@ -84,6 +104,15 @@ const ApproveHistory = () => {
return () => clearTimeout(timer);
}, []);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearchTerm(searchTerm);
}, 500); // Adjust delay as needed
return () => {
clearTimeout(handler);
};
}, [searchTerm]);
// ====================================================[Table Filter]================================================================
const filteredData = data?.data?.rows?.filter((item) => {
// Filter by name (case insensitive)
@@ -117,7 +146,7 @@ const ApproveHistory = () => {
"Status",
];
const extractedArray = filteredData?.map((item, idx) => ({
const extractedArray = data?.data?.rows?.map((item, idx) => ({
// id: item?.id,
"Sr.no": (
<Text
@@ -172,11 +201,7 @@ const ApproveHistory = () => {
</Box>
),
"Deposit Date": (
<Box
w={"100px"}
isTruncated={true}
display={"flex"}
>
<Box w={"100px"} isTruncated={true} display={"flex"}>
<Text as={"span"} color={"teal.900"}>
{formatDate(item?.transaction_date)}
</Text>
@@ -202,28 +227,27 @@ const ApproveHistory = () => {
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"}
{item?.spportFile_path && (
<Badge
px={2}
py={0.5}
textTransform={"inherit"}
fontWeight={500}
colorScheme={"forestGreen"}
>
<Box me={"1px"}
as="span"
cursor={"pointer"}
<Link
href={import.meta.env.VITE_IMAGE_URL + item?.spportFile_path}
isExternal
display={"flex"}
alignItems={"center"}
>
View
</Box>
<ExternalLinkIcon />
</Link>
</Badge>}
<Box me={"1px"} as="span" cursor={"pointer"}>
View
</Box>
<ExternalLinkIcon />
</Link>
</Badge>
)}
</Text>
),
Status: (
@@ -236,12 +260,12 @@ const ApproveHistory = () => {
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
? "green"
: item?.transactionStatus === "Pending"
? "yellow"
: item?.transactionStatus === "Reject"
? "red"
: "gray" // default border color if status doesn't match any condition
}
>
{item.transactionStatus}

View File

@@ -1,229 +1,239 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Link,
Text,
Tooltip,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
import { CheckIcon, CloseIcon, ExternalLinkIcon } from "@chakra-ui/icons";
import Pagination from "../../../Components/Pagination";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import DrawalRequestReject from "../../WithDrawal/DrawalRequest/DrawalRequestReject";
import NormalTable from "../../../Components/DataTable/NormalTable";
import DrawalRequestApprove from "../../WithDrawal/DrawalRequest/DrawalRequestApprove";
import { generateSerialNumber } from "../../../Constants/Constants";
import {
useGetApproveHistoryQuery,
useGetFawateerForMakerRequestQuery,
useGetFawateerRequestQuery,
} from "../../../Services/fawateer.request.service";
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
const ApproveHistoryMaker = () => {
const toast = useToast();
const { slideFromRight, approveHistory, setApproveHistory } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
const {
isOpen: isConfirmOpen,
onOpen: onConfirmOpen,
onClose: onConfirmClose,
} = useDisclosure();
const {
isOpen: isRejectOpen,
onOpen: onRejectOpen,
onClose: onRejectClose,
} = useDisclosure();
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 {
data,
isLoading: drawalRequestLoading,
error,
refetch
} = useGetFawateerForMakerRequestQuery(
{
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
searchTerm: debouncedSearchTerm,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
}
);
console.log(data);
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("");
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;
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",
});
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr.no",
"Client ID",
"First Name",
"Last Name",
"E-mail ID",
"Phone Number",
"Deposit Date",
"Deposit Amount (BHD)",
"Support Image",
"Status",
];
const extractedArray = data?.data?.rows?.map((item, idx) => ({
// id: item?.id,
"Sr.no": (
<Text
w={"auto"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{generateSerialNumber(idx, currentPage, pageSize)}
};
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>
),
"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={"50px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item.lastName}
</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
</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"}
@@ -236,123 +246,197 @@ import {
display={"flex"}
alignItems={"center"}
>
<Box me={"1px"}
as="span"
cursor={"pointer"}
>
<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"
</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}
/>
}
>
{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);
};
export default ApproveHistoryMaker;
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

@@ -104,6 +104,12 @@ const RequestApproveModal = ({ isOpen, onClose, firstField ,id}) => {
}
}, [data, reset]);
useEffect(() => {
if (!isOpen) {
reset();
}
}, [isOpen, reset]);
const heandleOnClose = () =>{
reset()
onClose()
@@ -130,11 +136,11 @@ const RequestApproveModal = ({ isOpen, onClose, firstField ,id}) => {
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your checkerComment...."}
placeholder={"Enter your checker Comment...."}
rounded={"md"}
resize={"none"}
maxLength={200}
/>
/>
{errors.checkerComment && (
<Text fontSize="xs" color="red">
{errors.checkerComment.message}

View File

@@ -2,6 +2,7 @@ import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
@@ -24,8 +25,18 @@ import { useDepositRejectMutation } from "../../../Services/deposit.request.serv
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({
comments: yup.string().required("Comment is required"),
// 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}) => {
@@ -36,12 +47,20 @@ const RequestRejectModal = ({ isOpen, onClose, firstField ,id}) => {
const {
register,
reset,
watch,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
useEffect(() => {
if (!isOpen) {
reset(); // Clear the form state
}
}, [isOpen, reset]);
const [ rejectFawateer ] = useRejectCommentMutation()
@@ -101,7 +120,7 @@ const RequestRejectModal = ({ isOpen, onClose, firstField ,id}) => {
reset()
onClose()
}
return (
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
<ModalOverlay />
@@ -126,12 +145,17 @@ const RequestRejectModal = ({ isOpen, onClose, firstField ,id}) => {
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>

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

@@ -217,7 +217,7 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, da
isOpen={alert}
onClose={() => setAlert(false)}
alertHandler={handleSave}
message={"Are you sure you want to update this artifact?"}
message={"Are you sure you want to add this artifact?"}
isLoading={isLoading}
/>
</>

View File

@@ -20,6 +20,7 @@ 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();
@@ -87,7 +88,8 @@ const IOCashDetails = () => {
Pending
{IODetails?.ioCashStatusHistory?.Pending.length > 0 && (
<Badge rounded={"sm"} colorScheme="forestGreen" ms={2}>
{IODetails?.ioCashStatusHistory?.Pending.length !== 0 && IODetails?.ioCashStatusHistory?.Pending.length}
{IODetails?.ioCashStatusHistory?.Pending.length !== 0 &&
IODetails?.ioCashStatusHistory?.Pending.length}
</Badge>
)}
{/* <Badge rounded={"sm"} colorScheme="forestGreen" ms={2}>
@@ -105,7 +107,8 @@ const IOCashDetails = () => {
</Tab>
</TabList>
{IODetails?.isInvestedAmount
? localStorage?.getItem("role") === "Maker" && (
? isMaker() &&
IODetails?.ioSatatus !== "Exited" && (
<Button
onClick={handleAdd}
leftIcon={<AddIcon />}

View File

@@ -38,6 +38,7 @@ 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();
@@ -104,8 +105,7 @@ const Pending = () => {
"Comments",
"Update By",
"Update On",
...(localStorage?.getItem('role')!=="Maker" ? ["Actions"] : []),
...(!isMaker() ? ["Actions"] : []),
];
const extractedArray = filteredData?.map((item, index) => ({
@@ -131,9 +131,9 @@ const Pending = () => {
$
</Badge>
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
),
Comments: (
@@ -166,64 +166,69 @@ const Pending = () => {
),
Actions: (
<Box display={"flex"} justifyContent={"center"}>
{localStorage?.getItem("role") !== "Maker" ? <Box>
{index===0&&<Box display={"flex"} justifyContent={"center"} gap={2}>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="Approve"
bg="#fff"
color={"green.500"}
placement="left-start"
>
{!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="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"}
@@ -235,7 +240,8 @@ const Pending = () => {
}}
>
<ViewIcon me={"4px"} /> View
</Button>}
</Button>
)}
</Box>
),
}));
@@ -313,9 +319,9 @@ const Pending = () => {
$
</Badge>
{parseFloat(IODetails?.ioCash || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Th>
<Th
textAlign={"center"}

View File

@@ -119,7 +119,7 @@ import AddCaseDetails from "./AddCaseDetails";
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(IODetails?.ioCash || 0).toLocaleString(undefined, {
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}

View File

@@ -12,11 +12,12 @@ import {
useUpdateIOMutation,
} from "../../../Services/io.service";
import ToastBox from "../../../Components/ToastBox";
import {
useToast,
} from "@chakra-ui/react";
import { useToast } from "@chakra-ui/react";
import { formatDatee } from "../../../Components/FormField";
import { formatDateToYYYYMMDD, removeTrailingZeros } from "../../../Constants/Constants";
import {
formatDateToYYYYMMDD,
removeTrailingZeros,
} from "../../../Constants/Constants";
const schema = yup.object().shape({
investmentNameEnglish: yup
@@ -25,7 +26,7 @@ const schema = yup.object().shape({
.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
investmentNameArabic: yup
.string()
.required("IO name in Arabic is required")
.min(3, "IO name in Arabic must be at least 3 characters long")
@@ -42,15 +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"),
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'),
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")
@@ -69,28 +70,25 @@ 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 handleInputChangeCreate = (index, newValue) => {
const updatedValues = [...values];
updatedValues[index].value = newValue;
setValues(updatedValues);
console.log(values);
console.log(values);
};
const handleInputChangeEdit = (index, newValue) => {
@@ -143,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,
@@ -154,18 +155,19 @@ const IODetails = ({ enableNextTab, index, data }) => {
}
);
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 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
@@ -173,74 +175,67 @@ const IODetails = ({ enableNextTab, index, data }) => {
.required("IO name in English is required")
.min(3, "IO name in English must be at least 3 characters long")
.max(150, "IO name in English must be at most 150 characters long"),
investmentNameArabic: yup
.string()
.required("IO name in Arabic is required")
.min(3, "IO name in Arabic must be at least 3 characters long")
.max(50, "IO name in Arabic must be at most 50 characters long"),
descriptionEnglish: yup
.string()
.required("Description in English is required")
.min(10, "Description in English must be at least 10 characters long")
.max(1000, "Description in English must be at most 1000 characters long"),
descriptionArabic: yup
.string()
.required("Description in Arabic is required")
.min(10, "Description in Arabic must be at least 10 characters long")
.max(2000, "Description in Arabic must be at most 500 characters long"),
expectedReturnArabic: yup
.string()
.required("Expected return in Arabic is required"),
goalAmount: yup
.number()
.typeError("Goal Amount is must be number")
.required('Goal amount is required')
.positive('Goal amount must be a positive number')
.min(IObyID?.data?.totalAmtInvestmentInUSD, `Goal amount should not be lesser then amount raised ${IObyID?.data?.totalAmtInvestmentInUSD}`),
closingDate: yup
.date()
.notRequired("Closing date is required")
.min(new Date(), "Closing date cannot be in the past"),
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
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 [values, setValues] = useState(id?minInvestmentById:miniValue);
const [values, setValues] = useState(id ? minInvestmentById : miniValue);
console.log(values);
const formatNumber = (num) => {
// Remove non-numeric characters and format with commas
return num.replace(/\D/g, '')
.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return num.replace(/\D/g, "").replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
// console.log(values);
@@ -254,10 +249,11 @@ const IODetails = ({ enableNextTab, index, data }) => {
formState: { errors },
} = useForm({
resolver: yupResolver(id ? schemaEdit : schema),
mode: "all",
});
useEffect(() => {
setIOloading(IObyIDisLoading)
setIOloading(IObyIDisLoading);
setIODetails({
...IObyID?.data,
});
@@ -278,10 +274,9 @@ const IODetails = ({ enableNextTab, index, data }) => {
minInvestmentAmount: IObyID?.data?.minInvestmentAmount,
holdingPeriodArabic: IObyID?.data?.minInvestmentAmount,
expectedReturnArabic: IObyID?.data?.minInvestmentAmount,
isShariah: IObyID?.data?.isShariah
isShariah: IObyID?.data?.isShariah,
});
}
}, [id, IObyID]);
//=======================[ Creator ]
@@ -294,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)",
@@ -306,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",
@@ -317,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)",
@@ -329,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",
@@ -342,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)",
@@ -355,11 +359,12 @@ 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",
@@ -372,7 +377,7 @@ const IODetails = ({ enableNextTab, index, data }) => {
{
label: "Expected Return (Arabic)",
name: "expectedReturnArabic",
name: "expectedReturnArabic",
type: "text",
isRequired: true,
arabic: true,
@@ -382,16 +387,15 @@ const IODetails = ({ enableNextTab, index, data }) => {
},
{
label: "Shariah",
name: "isShariah",
name: "isShariah",
type: "checkBox",
value:IObyID?.data?.isShariah,
value: IObyID?.data?.isShariah,
// isRequired: true,
section: " ",
width: "32.3%",
value: IObyID?.data?.isShariah,
},
{
label: "Investment Type",
placeHolder: "Select option",
@@ -429,19 +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:true
closingDate:id ? null : true
},
{
label: "ISIN",
placeHolder: "",
name: "ISIN",
type: "text",
align:"right",
align: "right",
section: " ",
width: "32.3%",
},
@@ -453,8 +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.`
maxLength: 20,
helperText: `Maximum length should be 20 characters. You have entered ${
watch()?.InvestmentDetails?.length || 0
} characters.`,
},
{
@@ -463,10 +469,10 @@ const IODetails = ({ enableNextTab, index, data }) => {
name: "table",
type: "table",
section: " ",
width: "100%",
width: "100%",
isRequired: true,
options: investmentTypeOptions,
handleInputChange:id ? handleInputChangeEdit : handleInputChangeCreate,
handleInputChange: id ? handleInputChangeEdit : handleInputChangeCreate,
value: values,
},
@@ -479,8 +485,10 @@ 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.`,
},
];
const groupedFields = formFields.reduce((groups, field) => {
@@ -493,30 +501,29 @@ const IODetails = ({ enableNextTab, index, data }) => {
}, {});
const onSubmit = async (data) => {
delete data.table;
setIsLoading(true);
const updatedMinAmount = values?.map(({id, value, _id})=>{
const updatedMinAmount = values?.map(({ id, value, _id }) => {
return {
id:_id,
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,
closingDate: formatDateToYYYYMMDD(data.closingDate)
minInvestmentAmt: updatedMinAmount,
closingDate: formatDateToYYYYMMDD(data.closingDate),
};
// console.log(formData);
if (id) {
console.log("========================",formData);
console.log("========================", formData);
const res = await updateIO({ data: formData, id });
console.log(res);
if (res?.data?.statusCode === 200) {
@@ -526,20 +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"} />,
});
} else if(res?.error?.status === 500){
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"} />,
render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} />
),
});
}
} else {
try {
console.log("========================",formData);
console.log("========================", formData);
const res = await creatIO(formData);
console.log(res?.error?.status);
if (res?.data?.statusCode === 200) {
@@ -549,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) {
@@ -566,7 +581,6 @@ const IODetails = ({ enableNextTab, index, data }) => {
}
}
// ==========================
// if (params?.id) {
// return enableNextTab(index);
@@ -584,9 +598,8 @@ const IODetails = ({ enableNextTab, index, data }) => {
};
return IObyIDisLoading ? (
<FullscreenLoaders height={'70vh'} />
<FullscreenLoaders height={"70vh"} />
) : (
<FormInputMain
p={0.1}
w={250}

View File

@@ -4,10 +4,7 @@ import {
Box,
Button,
HStack,
Input,
Table,
Tag,
Tbody,
Input,
Text,
Th,
Tooltip,
@@ -22,7 +19,7 @@ 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 * as XLSX from "xlsx";
import ToastBox from "../../../../Components/ToastBox";
import AddCashDetails from "../AddCashDetails";
import { debounce } from "../../../Admin/Contact";

View File

@@ -75,6 +75,7 @@ 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();
@@ -152,7 +153,8 @@ const IONAVDetails = () => {
</Tab>
</TabList>
{IODetails?.isInvestedAmount
? localStorage?.getItem("role") === "Maker" && (
? isMaker() &&
IODetails?.ioSatatus !== "Exited" && (
<Button
onClick={handleAdd}
leftIcon={<AddIcon />}

View File

@@ -22,6 +22,7 @@ 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();
@@ -90,7 +91,7 @@ const Pending = () => {
"Investment Closed",
"Comments",
"Updated By",
...(localStorage?.getItem("role") !== "Maker" ? ["Status"] : []),
...(!isMaker() ? ["Status"] : []),
];
const extractedArray = filteredData?.map((item, index) => ({
@@ -111,9 +112,9 @@ const Pending = () => {
$
</Badge>
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
),
"Last Nav Update": (
@@ -161,66 +162,80 @@ const Pending = () => {
{item?.modifier?.firstName}
</Text>
),
Status: (
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>
<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>
<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>
</Box>
),
}));

View File

@@ -1,32 +1,26 @@
import { ViewIcon } from "@chakra-ui/icons";
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Table,
Tag,
Tbody,
Text,
Th,
Tooltip,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import { CheckIcon, CloseIcon, ViewIcon } from "@chakra-ui/icons";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import { isMaker } from "../../../../Constants/Constants";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import RequestApproveModal from "./RequestApproveModal";
import RequestRejectModal from "./RequestRejectModal";
import ViewAmountInvested from "./ViewAmountInvested";
import ViewCancel from "./ViewCancel";
import ViewDistributionInvestor from "./ViewDistributionInvestor";
import ViewExit from "./ViewExit";
import ViewCancel from "./ViewCancel";
const formatDate = (date) => new Date(date).toLocaleDateString();
@@ -42,6 +36,7 @@ const Pending = () => {
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const [distributedAmt, setDistributedAmt] = useState();
const {
isOpen: isConfirmOpen,
@@ -92,11 +87,6 @@ const Pending = () => {
});
};
console.log(
"==============panding",
IODetails?.ioTransactionRecords?.Pending
);
// Table filter
// const filteredData = IODetails?.ioTransactionRecords?.Pending?.filter((item) => {
// // Filter by name (case insensitive)
@@ -127,11 +117,7 @@ const Pending = () => {
</Text>
),
"Transaction Date": (
<Text
as={"span"}
color={"gray.600"}
fontWeight={"500"}
>
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
{formatDate(item?.transactionDate)}
</Text>
),
@@ -151,9 +137,19 @@ const Pending = () => {
})}
</Text>
),
// Amount:(
// <div>
// <ViewDistributionInvestor amount={item?.transactionAmount} />
// </div>
// ),
"Created By": (
<Text
textTransform={'capitalize'} w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
<Text
textTransform={"capitalize"}
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
{item?.creator?.firstName}
</Text>
),
@@ -163,8 +159,13 @@ const Pending = () => {
</Text>
),
"Approved By": (
<Text
textTransform={'capitalize'} w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
<Text
textTransform={"capitalize"}
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
{item?.modifier?.firstName}
</Text>
),
@@ -188,6 +189,7 @@ const Pending = () => {
onInvestmentOpen();
} else if (item?.transactionType === "Distribution To Investor") {
onDistInvestorOpen();
setDistributedAmt(item?.transactionAmount);
} else if (item?.transactionType === "Exit") {
onExitOpen();
} else if (item?.transactionType === "Cancel") {
@@ -195,13 +197,16 @@ const Pending = () => {
}
}}
>
{localStorage?.getItem("role") === "Maker" ? <ViewIcon me={"4px"} /> : null} {localStorage?.getItem("role") === "Maker" ? "View" : "Approve / Reject"}
{isMaker() ? <ViewIcon me={"4px"} /> : null}{" "}
{isMaker() ? "View" : "Approve / Reject"}
</Button>
</Box>
),
})
);
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
@@ -262,20 +267,13 @@ const Pending = () => {
id={actionId}
/>
<ViewDistributionInvestor
isOpen={isDistInvestorOpen}
onClose={onDistInvestorClose}
id={actionId}
/>
<ViewExit
isOpen={isExitOpen}
onClose={onExitClose}
isOpen={isDistInvestorOpen}
onClose={onDistInvestorClose}
id={actionId}
amount={distributedAmt}
/>
<ViewCancel
isOpen={isCancelOpen}
onClose={onCancelClose}
id={actionId}
/>
<ViewExit isOpen={isExitOpen} onClose={onExitClose} id={actionId} />
<ViewCancel isOpen={isCancelOpen} onClose={onCancelClose} id={actionId} />
<RequestApproveModal
// data={data?.data?.rows}
isOpen={isConfirmOpen}

View File

@@ -27,6 +27,7 @@ import CurrencyInput from "../../../../Components/CurrencyInput";
import RequestRejectModal from "./RequestRejectModal";
import ApproveInvestedModal from "./ApproveInvestedModal";
import { formatDate } from "../../../Master/Sponser/Sponsers";
import { encryptString, isMaker } from "../../../../Constants/Constants";
// Validation schema
const validationSchema = yup.object().shape({
@@ -116,7 +117,6 @@ const ViewAmountInvested = ({ isOpen, onClose, id: investorId }) => {
};
// const formatDate = (date) => new Date(date).toLocaleDateString();
const handleAmountChange = (e) => {
// e might be an object or just a value, handle both cases
@@ -236,41 +236,43 @@ const ViewAmountInvested = ({ isOpen, onClose, id: investorId }) => {
/>
</FormControl>
{localStorage?.getItem("role") !== "Maker" && <ModalFooter>
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onConfirmOpen();
}}
colorScheme="forestGreen"
variant={"solid"}
cursor={"pointer"}
>
Approve
</Button>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onRejectOpen();
}}
>
Reject
</Button>
</Box>
</ModalFooter>}
{!isMaker() && (
<ModalFooter>
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onConfirmOpen();
}}
colorScheme="forestGreen"
variant={"solid"}
cursor={"pointer"}
>
Approve
</Button>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onRejectOpen();
}}
>
Reject
</Button>
</Box>
</ModalFooter>
)}
</form>
</ModalBody>
</ModalContent>

View File

@@ -1,127 +1,128 @@
import {
Badge,
Box,
Button,
HStack,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Table,
Tbody,
Text,
Th,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import NormalData from "../../../../Components/DataTable/NormalTable";
import { useContext, useState } from "react";
import {
useExitIOTransactionMutation,
useGetDistributedToInvestorMutation,
useGetDistributionInvestorMutation,
useGetIOByIdQuery,
} from "../../../../Services/io.service";
import { useParams } from "react-router-dom";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import ToastBox from "../../../../Components/ToastBox";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
Badge,
Box,
Button,
HStack,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Table,
Tbody,
Text,
Th,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import NormalData from "../../../../Components/DataTable/NormalTable";
import { useContext, useState } from "react";
import {
useExitIOTransactionMutation,
useGetDistributedToInvestorMutation,
useGetDistributionInvestorMutation,
useGetIOByIdQuery,
} from "../../../../Services/io.service";
import { useParams } from "react-router-dom";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import ToastBox from "../../../../Components/ToastBox";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import ApprovedCancelTransaction from "./ApprovedCancelTransaction";
import RequestRejectModal from "./RequestRejectModal";
const ViewCancel = ({ isOpen, onClose,id:cancleId }) => {
const params = useParams();
const toast = useToast();
const id = params?.id;
const [isCalculateLoading, setIsCalculateLoading] = useState(false);
const [isFinalCalculateLoading, setIsFinalCalculateLoading] = useState(false);
const [calcualtedData, setCalculatedDate] = useState(null);
const [isCalcualtedData, setIsCalcualtedData] = useState(false);
const { investors, setInvestors, slideFromRight, IODetails } =
import { encryptString, isMaker } from "../../../../Constants/Constants";
const ViewCancel = ({ isOpen, onClose, id: cancleId }) => {
const params = useParams();
const toast = useToast();
const id = params?.id;
const [isCalculateLoading, setIsCalculateLoading] = useState(false);
const [isFinalCalculateLoading, setIsFinalCalculateLoading] = useState(false);
const [calcualtedData, setCalculatedDate] = useState(null);
const [isCalcualtedData, setIsCalcualtedData] = useState(false);
const { investors, setInvestors, slideFromRight, IODetails } =
useContext(GlobalStateContext);
const [actionId, setActionId] = useState(false);
const [actionId, setActionId] = useState(false);
const {
isOpen: isConfirmOpen,
onOpen: onConfirmOpen,
onClose: onConfirmClose,
} = useDisclosure();
const {
isOpen: isRejectOpen,
onOpen: onRejectOpen,
onClose: onRejectClose,
} = useDisclosure();
const investorExit = yup.object().shape({
amount: yup
.string()
.required("Amount is required")
.test(
"max",
`Distribution amount should not be greater than IO cash amount ${IODetails?.ioCash}`,
function (value) {
const { ioCash } = IODetails || {}; // Safely get ioCash
if (value && ioCash) {
return parseFloat(value) <= parseFloat(ioCash); // Ensure both are compared as numbers
}
return true; // If ioCash is not available, skip validation
const {
isOpen: isConfirmOpen,
onOpen: onConfirmOpen,
onClose: onConfirmClose,
} = useDisclosure();
const {
isOpen: isRejectOpen,
onOpen: onRejectOpen,
onClose: onRejectClose,
} = useDisclosure();
const investorExit = yup.object().shape({
amount: yup
.string()
.required("Amount is required")
.test(
"max",
`Distribution amount should not be greater than IO cash amount ${IODetails?.ioCash}`,
function (value) {
const { ioCash } = IODetails || {}; // Safely get ioCash
if (value && ioCash) {
return parseFloat(value) <= parseFloat(ioCash); // Ensure both are compared as numbers
}
),
});
const {
control,
handleSubmit,
formState: { errors },
reset,
} = useForm({
resolver: yupResolver(investorExit),
});
useEffect(() => {
console.log("hiit useEffectc");
if (id && IODetails) {
handleCalculate(id, {
amount: IODetails?.ioMVNAV,
});
}
reset({
return true; // If ioCash is not available, skip validation
}
),
});
const {
control,
handleSubmit,
formState: { errors },
reset,
} = useForm({
resolver: yupResolver(investorExit),
});
useEffect(() => {
console.log("hiit useEffectc");
if (id && IODetails) {
handleCalculate(id, {
amount: IODetails?.ioMVNAV,
});
}, [IODetails, id]);
const handleCalculate = async (id, data) => {
try {
const res = await getDistributionInvestment({ id, data });
console.log(res?.data?.data);
if (res?.error?.status === 401) {
setIsCalculateLoading(false);
setIsCalcualtedData(false);
} else if (res?.data?.statusCode === 200) {
setCalculatedDate(res?.data?.data);
setIsCalculateLoading(false);
setIsCalcualtedData(true);
}
} catch (error) {}
};
const [getDistributionInvestment] = useGetDistributionInvestorMutation();
const investor = yup.object().shape({
amount: yup.string().required("Amount is required"),
}
reset({
amount: IODetails?.ioMVNAV,
});
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
}, [IODetails, id]);
const handleCalculate = async (id, data) => {
try {
const res = await getDistributionInvestment({ id, data });
console.log(res?.data?.data);
if (res?.error?.status === 401) {
setIsCalculateLoading(false);
setIsCalcualtedData(false);
} else if (res?.data?.statusCode === 200) {
setCalculatedDate(res?.data?.data);
setIsCalculateLoading(false);
setIsCalcualtedData(true);
}
} catch (error) {}
};
const [getDistributionInvestment] = useGetDistributionInvestorMutation();
const investor = yup.object().shape({
amount: yup.string().required("Amount is required"),
});
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Client ID",
"First name",
"Last name",
@@ -133,221 +134,223 @@ import RequestRejectModal from "./RequestRejectModal";
"Distribution Percent",
"Total Return",
"Total return on Investment",
];
const extractedArray = IODetails?.investors?.map((item, index) => ({
id: item?.id,
"Client ID": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item?.clientReference_id}
</Text>
),
"First name": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.firstName}
</Text>
),
"Last name": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.lastName}
</Text>
),
"Investment amount": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{/* {`$${formatCurrency(item.InvestedAmount_USD)}`} */}
{`${parseFloat(item.InvestedAmount_USD || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text>
),
Percentage: (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.Investor_Holidings} %
</Text>
),
"Market Value": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{`${parseFloat(item.Market_Value || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text>
),
"Return on Investment": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
h={6}
className="d-flex align-items-center web-text-small"
>
{item.Return_On_Investment || 0} %
</Text>
),
Distribution: (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{/* {`$${item.Distribution_Amt}`} */}
{`${parseFloat(item.Distribution_Amt || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text>
),
"Distribution Percent": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{/* {`$${item.Distribution_Amt}`} */}
{`${parseFloat(item.Distribution_Per || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})} %`}
</Text>
),
"Total Return": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{/* {`$${formatCurrency(item.Total_Return) || 0}`} */}
{`${parseFloat(item.Total_Return || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text>
),
"Total return on Investment": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.Total_Return_On_Investment || 0} %
</Text>
),
}));
const handleClose = () => {
onClose();
setIsFinalCalculateLoading(false);
setIsCalcualtedData(false);
};
return (
<Modal size={"xl"} isOpen={isOpen} onClose={handleClose} >
<ModalOverlay />
<ModalContent maxW={1000}>
<ModalHeader fontSize={"md"}>Cancel Transaction</ModalHeader>
<ModalCloseButton />
<ModalBody>
<NormalData
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
/>
</ModalBody>
{localStorage?.getItem("role") !== "Maker" && <ModalFooter pt={0}>
];
const extractedArray = IODetails?.investors?.map((item, index) => ({
id: item?.id,
"Client ID": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item?.clientReference_id}
</Text>
),
"First name": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.firstName}
</Text>
),
"Last name": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.lastName}
</Text>
),
"Investment amount": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{/* {`$${formatCurrency(item.InvestedAmount_USD)}`} */}
{`${parseFloat(item.InvestedAmount_USD || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text>
),
Percentage: (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.Investor_Holidings} %
</Text>
),
"Market Value": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{`${parseFloat(item.Market_Value || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text>
),
"Return on Investment": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
h={6}
className="d-flex align-items-center web-text-small"
>
{item.Return_On_Investment || 0} %
</Text>
),
Distribution: (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{/* {`$${item.Distribution_Amt}`} */}
{`${parseFloat(item.Distribution_Amt || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text>
),
"Distribution Percent": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{/* {`$${item.Distribution_Amt}`} */}
{`${parseFloat(item.Distribution_Per || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})} %`}
</Text>
),
"Total Return": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{/* {`$${formatCurrency(item.Total_Return) || 0}`} */}
{`${parseFloat(item.Total_Return || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text>
),
"Total return on Investment": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item.Total_Return_On_Investment || 0} %
</Text>
),
}));
const handleClose = () => {
onClose();
setIsFinalCalculateLoading(false);
setIsCalcualtedData(false);
};
return (
<Modal size={"xl"} isOpen={isOpen} onClose={handleClose}>
<ModalOverlay />
<ModalContent maxW={1000}>
<ModalHeader fontSize={"md"}>Cancel Transaction</ModalHeader>
<ModalCloseButton />
<ModalBody>
<NormalData
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
/>
</ModalBody>
{!isMaker() && (
<ModalFooter pt={0}>
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onConfirmOpen();
}}
colorScheme="forestGreen"
variant={"solid"}
cursor={"pointer"}
>
Approve
</Button>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onRejectOpen();
}}
>
Reject
</Button>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onConfirmOpen();
}}
colorScheme="forestGreen"
variant={"solid"}
cursor={"pointer"}
>
Approve
</Button>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onRejectOpen();
}}
>
Reject
</Button>
</Box>
</ModalFooter>}
</ModalContent>
<ApprovedCancelTransaction
</ModalFooter>
)}
</ModalContent>
<ApprovedCancelTransaction
isOpen={isConfirmOpen}
onClose={onConfirmClose}
onBigModalClose={onClose}
@@ -359,9 +362,8 @@ import RequestRejectModal from "./RequestRejectModal";
onBigModalClose={onClose}
id={cancleId}
/>
</Modal>
);
};
export default ViewCancel;
</Modal>
);
};
export default ViewCancel;

View File

@@ -23,8 +23,9 @@ import { yupResolver } from "@hookform/resolvers/yup";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import ApproveDistrubationModal from "./ApproveDistrubationModal";
import RequestRejectModal from "./RequestRejectModal";
import { encryptString, isMaker } from "../../../../Constants/Constants";
const ViewDistributionInvestor = ({ isOpen, onClose,id:exitId }) => {
const ViewDistributionInvestor = ({ isOpen, onClose, id: exitId, amount }) => {
const params = useParams();
const toast = useToast();
const id = params?.id;
@@ -71,19 +72,15 @@ const ViewDistributionInvestor = ({ isOpen, onClose,id:exitId }) => {
});
useEffect(() => {
console.log("hiit useEffectc");
// handleCalculate(id, {
// amount: IODetails?.ioMVNAV,
// });
if (id && IODetails) {
handleCalculate(id, {
amount: IODetails?.ioMVNAV,
amount: Math.abs(amount),
});
}
}
reset({
amount: IODetails?.ioMVNAV,
amount: amount,
});
}, [IODetails, id]);
}, [IODetails, id, amount]);
const handleCalculate = async (id, data) => {
try {
@@ -111,7 +108,7 @@ const ViewDistributionInvestor = ({ isOpen, onClose,id:exitId }) => {
"Last Name",
"Amount",
"Holding (%)",
"Distriution Amt($)",
"Distribution Amt($)",
"Yeild (%)",
];
@@ -172,7 +169,7 @@ const ViewDistributionInvestor = ({ isOpen, onClose,id:exitId }) => {
</Text>
</Box>
),
"Distriution Amt($)": (
"Distribution Amt($)": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.distribution_amt?.toLocaleString(undefined, {
@@ -200,7 +197,6 @@ const ViewDistributionInvestor = ({ isOpen, onClose,id:exitId }) => {
setIsFinalCalculateLoading(false);
setIsCalcualtedData(false);
};
return (
<Modal size={"xl"} isOpen={isOpen} onClose={handleClose}>
@@ -218,41 +214,43 @@ const ViewDistributionInvestor = ({ isOpen, onClose,id:exitId }) => {
/>
</ModalBody>
{/* ...(localStorage?.getItem("role") !== "Maker" ? ["Status"] : []), */}
{localStorage?.getItem("role") !== "Maker" &&<ModalFooter pt={0}>
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onConfirmOpen();
}}
colorScheme="forestGreen"
variant={"solid"}
cursor={"pointer"}
>
Approve
</Button>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onRejectOpen();
}}
>
Reject
</Button>
</Box>
</ModalFooter>}
{!isMaker() && (
<ModalFooter pt={0}>
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onConfirmOpen();
}}
colorScheme="forestGreen"
variant={"solid"}
cursor={"pointer"}
>
Approve
</Button>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onRejectOpen();
}}
>
Reject
</Button>
</Box>
</ModalFooter>
)}
</ModalContent>
<ApproveDistrubationModal
isOpen={isConfirmOpen}

View File

@@ -1,329 +1,332 @@
import {
Alert,
AlertIcon,
Box,
Button,
FormControl,
FormErrorMessage,
FormLabel,
HStack,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Switch,
Table,
Tbody,
Text,
Textarea,
Th,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import NormalData from "../../../../Components/DataTable/NormalTable";
import { useContext, useState } from "react";
import { AddIcon } from "@chakra-ui/icons";
import {
useExitIOTransactionMutation,
useGetDistributedToInvestorMutation,
useGetDistributionInvestorMutation,
useUpdateExitToInvestorMutation,
} from "../../../../Services/io.service";
import { useParams } from "react-router-dom";
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import ToastBox from "../../../../Components/ToastBox";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
Alert,
AlertIcon,
Box,
Button,
FormControl,
FormErrorMessage,
FormLabel,
HStack,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Switch,
Table,
Tbody,
Text,
Textarea,
Th,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import NormalData from "../../../../Components/DataTable/NormalTable";
import { useContext, useState } from "react";
import { AddIcon } from "@chakra-ui/icons";
import {
useExitIOTransactionMutation,
useGetDistributedToInvestorMutation,
useGetDistributionInvestorMutation,
useUpdateExitToInvestorMutation,
} from "../../../../Services/io.service";
import { useParams } from "react-router-dom";
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import ToastBox from "../../../../Components/ToastBox";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import ApprovedExit from "./ApprovedExit";
import RequestRejectModal from "./RequestRejectModal";
const ViewExit = ({ isOpen, onClose ,id:investerId}) => {
const params = useParams();
const toast = useToast();
const id = params?.id;
const [isCalculateLoading, setIsCalculateLoading] = useState(false);
const [isFinalCalculateLoading, setIsFinalCalculateLoading] = useState(false);
const [calcualtedData, setCalculatedDate] = useState(null);
const [isCalcualtedData, setIsCalcualtedData] = useState(false);
const { IODetails } = useContext(GlobalStateContext);
const [actionId, setActionId] = useState(false);
import { encryptString, isMaker } from "../../../../Constants/Constants";
const {
isOpen: isConfirmOpen,
onOpen: onConfirmOpen,
onClose: onConfirmClose,
} = useDisclosure();
const {
isOpen: isRejectOpen,
onOpen: onRejectOpen,
onClose: onRejectClose,
} = useDisclosure();
const {
control,
handleSubmit,
formState: { errors },
reset,
} = useForm({
resolver: yupResolver(),
});
useEffect(() => {
console.log("hiit useEffectc");
if (id && IODetails) {
const ViewExit = ({ isOpen, onClose, id: investerId }) => {
const params = useParams();
const toast = useToast();
const id = params?.id;
const [isCalculateLoading, setIsCalculateLoading] = useState(false);
const [isFinalCalculateLoading, setIsFinalCalculateLoading] = useState(false);
const [calcualtedData, setCalculatedDate] = useState(null);
const [isCalcualtedData, setIsCalcualtedData] = useState(false);
const { IODetails } = useContext(GlobalStateContext);
const [actionId, setActionId] = useState(false);
const {
isOpen: isConfirmOpen,
onOpen: onConfirmOpen,
onClose: onConfirmClose,
} = useDisclosure();
const {
isOpen: isRejectOpen,
onOpen: onRejectOpen,
onClose: onRejectClose,
} = useDisclosure();
const {
control,
handleSubmit,
formState: { errors },
reset,
} = useForm({
resolver: yupResolver(),
});
useEffect(() => {
console.log("hiit useEffectc");
if (id && IODetails) {
handleCalculate(id, {
amount: IODetails?.ioMVNAV,
});
}
reset({
amount: IODetails?.ioMVNAV,
});
}, [IODetails, id]);
const handleCalculate = async (id, data) => {
try {
const res = await getDistributionInvestment({ id, data });
console.log(res?.data?.data);
if (res?.error?.status === 401) {
setIsCalculateLoading(false);
setIsCalcualtedData(false);
} else if (res?.data?.statusCode === 200) {
setCalculatedDate(res?.data?.data);
setIsCalculateLoading(false);
setIsCalcualtedData(true);
}
} catch (error) {}
};
const [getDistributionInvestment] = useGetDistributionInvestorMutation();
const investor = yup.object().shape({
amount: yup.string().required("Amount is required"),
}
reset({
amount: IODetails?.ioMVNAV,
});
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr No.",
"Client Id",
"First name",
"Last Name",
"Amount",
"Holding (%)",
"Exit Amt($)",
];
}, [IODetails, id]);
const extractedArray = calcualtedData?.data?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Box
w={9}
display={"flex"}
alignItems={"center"}
isTruncated={true}
h={25}
>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{index + 1}
</Text>
</Box>
),
"Client Id": (
<Box w={100} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.clientId}
</Text>
</Box>
),
"First name": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.firstName}
</Text>
</Box>
),
"Last Name": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.lastName}
</Text>
</Box>
),
Amount: (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.amount?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</Box>
),
"Holding (%)": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.investor_holidings?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}%
</Text>
</Box>
),
"Exit Amt($)": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.distribution_amt?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</Box>
),
}));
const onSubmit = async (data) => {
setIsCalculateLoading(true);
try {
const res = await getDistributionInvestment({ id, data });
console.log(res?.data?.data);
if (res?.error?.status === 401) {
toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} />
),
});
setIsCalculateLoading(false);
setIsCalcualtedData(false);
} else if (res?.data?.statusCode === 200) {
setCalculatedDate(res?.data?.data);
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
setIsCalculateLoading(false);
setIsCalcualtedData(true);
}
} catch (error) {}
};
const handleClose = () => {
onClose();
setIsFinalCalculateLoading(false);
setIsCalcualtedData(false);
};
console.log(id);
const handleCalculate = async (id, data) => {
try {
const res = await getDistributionInvestment({ id, data });
console.log(res?.data?.data);
return (
<Modal size={"xl"} isOpen={isOpen} onClose={handleClose} >
<ModalOverlay />
<ModalContent maxW={1000}>
<ModalHeader fontSize={"md"}>Exit Transaction</ModalHeader>
<ModalCloseButton />
<ModalBody>
{/* <Text as="label" mb="5px" fontSize="sm" fontWeight={500}>
if (res?.error?.status === 401) {
setIsCalculateLoading(false);
setIsCalcualtedData(false);
} else if (res?.data?.statusCode === 200) {
setCalculatedDate(res?.data?.data);
setIsCalculateLoading(false);
setIsCalcualtedData(true);
}
} catch (error) {}
};
const [getDistributionInvestment] = useGetDistributionInvestorMutation();
const investor = yup.object().shape({
amount: yup.string().required("Amount is required"),
});
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr No.",
"Client Id",
"First name",
"Last Name",
"Amount",
"Holding (%)",
"Exit Amt($)",
];
const extractedArray = calcualtedData?.data?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Box
w={9}
display={"flex"}
alignItems={"center"}
isTruncated={true}
h={25}
>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{index + 1}
</Text>
</Box>
),
"Client Id": (
<Box w={100} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.clientId}
</Text>
</Box>
),
"First name": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.firstName}
</Text>
</Box>
),
"Last Name": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.lastName}
</Text>
</Box>
),
Amount: (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.amount?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</Box>
),
"Holding (%)": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.investor_holidings?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
%
</Text>
</Box>
),
"Exit Amt($)": (
<Box minW={24} isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.distribution_amt?.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
</Box>
),
}));
const onSubmit = async (data) => {
setIsCalculateLoading(true);
try {
const res = await getDistributionInvestment({ id, data });
console.log(res?.data?.data);
if (res?.error?.status === 401) {
toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status={"error"} />
),
});
setIsCalculateLoading(false);
setIsCalcualtedData(false);
} else if (res?.data?.statusCode === 200) {
setCalculatedDate(res?.data?.data);
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
setIsCalculateLoading(false);
setIsCalcualtedData(true);
}
} catch (error) {}
};
const handleClose = () => {
onClose();
setIsFinalCalculateLoading(false);
setIsCalcualtedData(false);
};
console.log(id);
return (
<Modal size={"xl"} isOpen={isOpen} onClose={handleClose}>
<ModalOverlay />
<ModalContent maxW={1000}>
<ModalHeader fontSize={"md"}>Exit Transaction</ModalHeader>
<ModalCloseButton />
<ModalBody>
{/* <Text as="label" mb="5px" fontSize="sm" fontWeight={500}>
Amount to Distribute
</Text> */}
<HStack onSubmit={handleSubmit(onSubmit)} as={"form"} mb={4} alignItems={'center'}>
{/* <Input placeholder="$00.00" size={"sm"} className="col" /> */}
{/* <FormControl isInvalid={errors.amount} isRequired>*/}
<Text textAlign={"right"} fontSize={"sm"}>
Exit Amount :
</Text>
<Text
textAlign={"start"}
bg={"green.100"}
p={2}
rounded={"md"}
fontSize={"sm"}
pt={1}
pb={1}
fontWeight={600}
>
${" "}
{parseFloat(IODetails?.ioMVNAV || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
{/* </FormControl> */}
</HStack>
{/* {calcualtedData && ( */}
<NormalData
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
// total={<Total />}
// isLoading={isLoading}
/>
{/* ) } */}
</ModalBody>
{localStorage?.getItem("role") !== "Maker" && <ModalFooter pt={0}>
<HStack
onSubmit={handleSubmit(onSubmit)}
as={"form"}
mb={4}
alignItems={"center"}
>
{/* <Input placeholder="$00.00" size={"sm"} className="col" /> */}
{/* <FormControl isInvalid={errors.amount} isRequired>*/}
<Text textAlign={"right"} fontSize={"sm"}>
Exit Amount :
</Text>
<Text
textAlign={"start"}
bg={"green.100"}
p={2}
rounded={"md"}
fontSize={"sm"}
pt={1}
pb={1}
fontWeight={600}
>
${" "}
{parseFloat(IODetails?.ioMVNAV || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Text>
{/* </FormControl> */}
</HStack>
{/* {calcualtedData && ( */}
<NormalData
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
// total={<Total />}
// isLoading={isLoading}
/>
{/* ) } */}
</ModalBody>
{!isMaker() && (
<ModalFooter pt={0}>
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onConfirmOpen();
}}
colorScheme="forestGreen"
variant={"solid"}
cursor={"pointer"}
>
Approve
</Button>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onRejectOpen();
}}
>
Reject
</Button>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onConfirmOpen();
}}
colorScheme="forestGreen"
variant={"solid"}
cursor={"pointer"}
>
Approve
</Button>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onRejectOpen();
}}
>
Reject
</Button>
</Box>
</ModalFooter>}
</ModalContent>
<ApprovedExit
isOpen={isConfirmOpen}
onClose={onConfirmClose}
onBigModalClose={onClose}
id={investerId}
/>
<RequestRejectModal
isOpen={isRejectOpen}
onClose={onRejectClose}
onBigModalClose={onClose}
id={investerId}
/>
</Modal>
);
};
export default ViewExit;
</ModalFooter>
)}
</ModalContent>
<ApprovedExit
isOpen={isConfirmOpen}
onClose={onConfirmClose}
onBigModalClose={onClose}
id={investerId}
/>
<RequestRejectModal
isOpen={isRejectOpen}
onClose={onRejectClose}
onBigModalClose={onClose}
id={investerId}
/>
</Modal>
);
};
export default ViewExit;

View File

@@ -182,7 +182,7 @@ const Investors = ({ data }) => {
{item?.clientReference_id}
</Text>
),
"First name": (
"First Name": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
@@ -193,7 +193,7 @@ const Investors = ({ data }) => {
{item.firstName}
</Text>
),
"Last name": (
"Last Name": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
@@ -204,7 +204,7 @@ const Investors = ({ data }) => {
{item.lastName}
</Text>
),
"Investment amount": (
"Investment Amount": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
@@ -313,7 +313,7 @@ const Investors = ({ data }) => {
})}`}
</Text>
),
"Total return on Investment": (
"Total Return on Investment": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}

View File

@@ -1,57 +1,29 @@
import React, { useContext, useEffect, useState } from "react";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import {
Box,
Divider,
FormControl,
FormLabel,
Heading,
Input,
Select,
Textarea,
Button,
Text,
Image,
Tabs,
TabList,
Tab,
TabList,
TabPanel,
TabPanels,
Tooltip,
Switch,
Tabs,
useDisclosure,
} from "@chakra-ui/react";
import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";
import {
AddIcon,
CloseIcon,
DeleteIcon,
EditIcon,
ViewIcon,
WarningTwoIcon,
} from "@chakra-ui/icons";
import { TiWarning } from "react-icons/ti";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import React, { useContext, useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
import FormField from "../../../Components/FormField";
import { v4 as uuidv4 } from "uuid";
import AddIOCharges from "../AddIOCharges";
import FormInputMain from "../../../Components/FormInputMain";
import DataTable from "../../../Components/DataTable/DataTable";
import { debounce } from "../../Master/Sponser/AddSponser";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import { formatDate } from "../../../Components/Functions/UTCConvertor";
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 * as yup from "yup";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import Distribution from "./Destribution";
import InvestmentDocuments from "../InvestmentDocuments";
import InvestmentDocument from "./InvestmentDocument";
import Investors from "./Investors";
import IOArtifacts from "./IOArtifacts";
import IOCashDetails from "./IOCashDetails";
import IODetails from "./IODetails";
import IONAVDetails from "./IONAVDetails";
import KeyMerits from "./KeyMerits";
import { useAuthProfileQuery } from "../../Services/token.serivce";
import { encryptString } from "../../Constants/Constants";
const schema = yup.object().shape({
ioName: yup.string().required("Arabic name is required"),
@@ -59,7 +31,9 @@ const schema = yup.object().shape({
discription: yup.string().required("Description is required"),
discriptionArabic: yup.string().required("Arabic Description is required"),
typeName: yup.string().required("Investment type is required"),
typeNameArabic: yup.string().required("Investment type arabic name is required"),
typeNameArabic: yup
.string()
.required("Investment type arabic name is required"),
sponserName: yup.string().required("Sponsorer name is required"),
sponserNameArabic: yup
.string()
@@ -293,7 +267,7 @@ const EditViewIO = () => {
isRequired: true,
section: " ",
width: "32.3%",
},
},
{
label: "Name (Arabic)",
placeHolder: " ",

View File

@@ -360,7 +360,7 @@ const IOArtifactsAdd = ({
isOpen={alert}
onClose={handleAlertClose}
alertHandler={handleSave}
message={"Are you sure you want to update this artifact?"}
message={"Are you sure you want to add this artifact?"}
isLoading={loading}
/>
</>

View File

@@ -60,9 +60,9 @@ export const investmentDocSchema = yup.object().shape({
// return value && value.size <= 2 * 1024 * 1024; // 2MB in bytes
// })
fileName: yup.string().required("File name is required")
.max(30, "File name must be at most 30 characters"), // Maximum length validation,
.max(35, "File name must be at most 30 characters"), // Maximum length validation,
documentNameArabic: yup.string().required("File name Arabic is required")
.max(25, "File name must be at most 30 characters"),
.max(25, "File name must be at most 35 characters"),
});
const InvestmentDocuments = ({

View File

@@ -112,8 +112,18 @@ const AmountInvested = ({ isOpen, onClose }) => {
});
};
const handleModalClose = () => {
reset({
transactionDate: "",
Total_Amount: IODetails?.totalAmtInvestmentInUSD || 0,
amountInvested: 0,
IoCash: IODetails?.totalAmtInvestmentInUSD || 0,
});
onClose();
};
return (
<Modal isOpen={isOpen} onClose={onClose}>
<Modal isOpen={isOpen} onClose={handleModalClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader fontSize={"md"}>Amount Invested</ModalHeader>
@@ -246,7 +256,7 @@ const AmountInvested = ({ isOpen, onClose }) => {
>
Save
</Button>
<Button size={"sm"} rounded={"sm"} mr={3} onClick={onClose}>
<Button size={"sm"} rounded={"sm"} mr={3} onClick={handleModalClose}>
Close
</Button>
</ModalFooter>

View File

@@ -495,7 +495,7 @@ const DistributionInvestor = ({ isOpen, onClose }) => {
>
Save
</Button>
<Button size={"sm"} rounded={"sm"} mr={3} onClick={onClose}>
<Button size={"sm"} rounded={"sm"} mr={3} onClick={handleClose}>
Close
</Button>
</>

View File

@@ -17,6 +17,9 @@ import {
ModalHeader,
ModalOverlay,
FormErrorMessage,
Text,
Textarea,
Box,
} from "@chakra-ui/react";
import {
useGetIOprepopulateDataQuery,
@@ -37,6 +40,8 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => {
const { data } = useGetIOprepopulateDataQuery();
const [updateStatusIo] = useUpdateStatusIoMutation();
const [updateCancleStatus] = useUpdateCancleStatusToMutation();
const [message, setMessage] = useState(null);
const [messageError, setMessageError] = useState(null);
// useEffect(() => {
// setSelectedStatusId(status?.[0]?.id);
@@ -47,22 +52,26 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => {
setSelectedStatusId(id);
};
const handleSubmit = async () => {
const handleSubmit = async (data) => {
if (!selectedStatusId) {
setError("Please select status");
return;
}
if (selectedStatusId == 6 && !message) {
return setMessageError("message is required");
}
setError("");
setIsLoading(true);
try {
let res;
// If selectedItem is 'Cancelled', make the updateCancelStatus API call
if (selectedItem === "Cancelled") {
if (selectedItem === import.meta.env.VITE_STATUS_CANCELLED) {
res = await updateCancleStatus({
id
id: id,
data: { comments: message },
});
}
}
// Otherwise, make the updateStatusIo API call
else {
res = await updateStatusIo({
@@ -72,7 +81,7 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => {
id,
});
}
console.log("API Response:", res);
setIsLoading(false);
handleClose();
@@ -84,6 +93,8 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => {
const handleClose = () => {
setSelectedItem(null);
setSelectedStatusId(null);
setMessage(null);
setMessageError(null);
onClose();
setError("");
};
@@ -119,17 +130,18 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => {
mb={1.5}
textTransform={"none"}
colorScheme={
selectedItem === "Draft"
selectedItem === import.meta.env.VITE_STATUS_DRAFT
? "gray"
: selectedItem === "Processing"
: selectedItem ===
import.meta.env.VITE_STATUS_PROCESSING
? "yellow"
: selectedItem === "Open"
: selectedItem === import.meta.env.VITE_STATUS_OPEN
? "blue"
: selectedItem === "Closed"
: selectedItem === import.meta.env.VITE_STATUS_CLOSED
? "green"
: selectedItem === "Exited"
: selectedItem === import.meta.env.VITE_STATUS_EXITED
? "red"
: selectedItem === "Cancelled"
: selectedItem === import.meta.env.VITE_STATUS_CANCELLED
? "orange"
: "purple"
}
@@ -161,17 +173,19 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => {
mb={1.5}
textTransform={"none"}
colorScheme={
statusAdmin === "Draft"
statusAdmin === import.meta.env.VITE_STATUS_DRAFT
? "gray"
: statusAdmin === "Processing"
: statusAdmin ===
import.meta.env.VITE_STATUS_PROCESSING
? "yellow"
: statusAdmin === "Open"
: statusAdmin === import.meta.env.VITE_STATUS_OPEN
? "blue"
: statusAdmin === "Closed"
: statusAdmin === import.meta.env.VITE_STATUS_CLOSED
? "green"
: statusAdmin === "Exited"
: statusAdmin === import.meta.env.VITE_STATUS_EXITED
? "red"
: statusAdmin === "Cancelled"
: statusAdmin ===
import.meta.env.VITE_STATUS_CANCELLED
? "orange"
: "purple"
}
@@ -191,6 +205,24 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => {
{error}
</FormErrorMessage>
</FormControl>
{selectedItem === import.meta.env.VITE_STATUS_CANCELLED && (
<FormControl mt={5} isRequired>
<FormLabel fontSize={"sm"} fontWeight={400}>
Message
</FormLabel>
<Textarea
resize={"none"}
rounded={5}
size="sm"
onChange={(e) => setMessage(e.target.value)}
/>
</FormControl>
)}
{messageError && (
<Text fontSize={"sm"} color={"red"}>
{messageError}
</Text>
)}
</ModalBody>
<ModalFooter>
<Button

View File

@@ -97,7 +97,6 @@ const ViewIOTable = () => {
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
});
console.log(data);
// ===============================[ Table Header ]
const tableHeadRow = [
@@ -242,17 +241,17 @@ const ViewIOTable = () => {
textTransform={"none"}
// variant={"solid"}
colorScheme={
item?.ioStatus?.statusAdmin === "Draft"
item?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_DRAFT
? "gray"
: item?.ioStatus?.statusAdmin === "Processing"
: item?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_PROCESSING
? "yellow"
: item?.ioStatus?.statusAdmin === "Open"
: item?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_OPEN
? "blue"
: item?.ioStatus?.statusAdmin === "Closed"
: item?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_CLOSED
? "green"
: item?.ioStatus?.statusAdmin === "Exited"
: item?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_EXITED
? "red"
: item?.ioStatus?.statusAdmin === "Canclled"
: item?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_CANCELLED
? "orange"
: "purple"
}

View File

@@ -1,8 +1,6 @@
import {
Box,
Button,
Icon,
Input,
keyframes,
Stack,
Tab,
@@ -10,40 +8,29 @@ import {
TabPanel,
TabPanels,
Tabs,
Text,
useDisclosure,
useDisclosure
} from "@chakra-ui/react";
import { useContext, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import { useContext, useEffect, useState } from "react";
import FormInputView from "../../../Components/FormInputView";
import { useForm } from "react-hook-form"; // assuming react-hook-form is used
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import { ArrowBackIcon, RepeatIcon } from "@chakra-ui/icons";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import InvestmentDocument from "../CreateIO/InvestmentDocument";
import Investors from "../CreateIO/Investors";
import IOArtifacts from "../CreateIO/IOArtifacts";
import KeyMerits from "../CreateIO/KeyMerits";
import ViewIOdataHeader from "./ViewIOdataHeader";
import ViewIOdetails from "./ViewIOdetails";
import ViewIOdocs from "./ViewIOdocs";
import ViewKeyMerits from "./ViewKeyMerits";
import ViewIOartifacts from "./ViewIOartifacts";
import ViewInvestors from "./ViewInvestors";
import ViewIOcash from "./ViewIOcash";
import ViewIOnav from "./ViewIOnav";
import ViewDistribution from "./ViewDistribution";
import InvestmentDocument from "../CreateIO/InvestmentDocument";
import KeyMerits from "../CreateIO/KeyMerits";
import Investors from "../CreateIO/Investors";
import EditIO from "../EditIO/EditIO";
import IOArtifacts from "../CreateIO/IOArtifacts";
// import IOCashDetails from "../CreateIO/IOCashDetailsold";
// import IONAVDetails from "../CreateIO/IONAVDetailsOld";
import { useGetIOByIdQuery, useGetIOprepopulateDataQuery } from "../../../Services/io.service";
import UnderConstruction from "../../UnderConstruction";
import { GoDotFill } from "react-icons/go";
import {
useGetIOByIdQuery,
useGetIOprepopulateDataQuery,
} from "../../../Services/io.service";
import Destribution from "../CreateIO/Destribution";
import IOCashDetails from "../CreateIO/IOCashDetails/IOCashDetails";
import IONAVDetails from "../CreateIO/IONAVDetails/IONAVDetails";
import IOTransaction from "../CreateIO/IOTransaction/IOTransaction";
import { GoDotFill } from "react-icons/go";
const rotate = keyframes`
from {
@@ -69,7 +56,6 @@ const ViewIOdata = () => {
const [isEditing, setIsEditing] = useState(false);
const [isRefetchLoading, setIsRefetchLoading] = useState(false);
const { IODetails, setIODetails } = useContext(GlobalStateContext);
console.log(IODetails?.isInvestedAmount);
const tabs = [
{ label: "IO Details", content: <ViewIOdetails data={data?.data} /> },
@@ -193,13 +179,19 @@ const ViewIOdata = () => {
cursor={"pointer"}
/>
</Box> */}
<Stack position={"absolute"} right={1} bottom={1} direction="row" spacing={4}>
<Stack
position={"absolute"}
right={1}
bottom={1}
direction="row"
spacing={4}
>
<Button
isLoading={isRefetchLoading}
loadingText="Refresh"
colorScheme="forestGreen"
variant="solid"
size={'xs'}
size={"xs"}
onClick={handleRefresh}
fontWeight={400}
>

View File

@@ -1,60 +1,59 @@
import {
Button,
Divider,
Badge,
Box,
HStack,
Icon,
Image,
Menu,
MenuButton,
MenuItem,
MenuList,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Portal,
Text,
useDisclosure,
MenuItemOption,
MenuGroup,
MenuOptionGroup,
MenuDivider,
Badge,
Box,
Icon,
HStack,
useToast,
} from "@chakra-ui/react";
import header from "../../../assets/IOheader.png";
import { useContext, useRef } from "react";
import { GrGallery } from "react-icons/gr";
import { HiDotsVertical } from "react-icons/hi";
import { Link, useParams } from "react-router-dom";
import { useParams } from "react-router-dom";
import Loader01 from "../../../Components/Loaders/Loader01";
import ToastBox from "../../../Components/ToastBox";
import {
decryptString,
encryptString,
isMaker,
} from "../../../Constants/Constants";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import { useUpdateTransactionMutation } from "../../../Services/io.service";
import AmountInvested from "./HeaderModal/AmountInvested";
import FeesExpenses from "./HeaderModal/FeesExpenses";
import DistributionSponsor from "./HeaderModal/DistributionSponsor";
import Cancle from "./HeaderModal/Cancle";
import DistributionInvestor from "./HeaderModal/DistributionInvestor";
import DistributionSponsor from "./HeaderModal/DistributionSponsor";
import Exit from "./HeaderModal/Exit";
import FeesExpenses from "./HeaderModal/FeesExpenses";
import UpdateIONav from "./HeaderModal/UpdateIONav";
import UpdateIOStatus from "./HeaderModal/UpdateIOStatus";
import { useContext, useRef } from "react";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import Exit from "./HeaderModal/Exit";
import Cancle from "./HeaderModal/Cancle";
import { AddIcon } from "@chakra-ui/icons";
import { GrGallery } from "react-icons/gr";
import Loader01 from "../../../Components/Loaders/Loader01";
import { useUpdateTransactionMutation } from "../../../Services/io.service";
import ToastBox from "../../../Components/ToastBox";
import { useAuthProfileQuery } from "../../../Services/token.serivce";
// import { formatCurrency } from "../../../Components/CurrencyInput";
// import { removeTrailingZeros } from "../../../Constants/Constants";
const ViewIOdataHeader = ({ data, isLoading }) => {
const params = useParams()
const toast = useToast();
const id = params?.id
const params = useParams();
const toast = useToast();
const id = params?.id;
const { isOpen, onOpen, onClose } = useDisclosure();
const btnRef = useRef();
const { IODetails, isIOloading } = useContext(GlobalStateContext);
const { data: authProfile } = useAuthProfileQuery();
if (authProfile?.data?.role) {
localStorage.setItem("role", encryptString(authProfile.data.role));
} else {
console.warn("Role is undefined or null. Skipping localStorage update.");
}
const {
isOpen: isInvestmentOpen,
onOpen: onInvestmentOpen,
@@ -106,95 +105,83 @@ const ViewIOdataHeader = ({ data, isLoading }) => {
fontSize: "0.875rem",
fontWeight: "400",
};
const [updateTransaction] = useUpdateTransactionMutation();
console.log(
import.meta.env.VITE_IMAGE_URL +
IODetails?.artifactsImage?.[0]?.artifactPathName
);
const [updateTransaction] = useUpdateTransactionMutation()
const handleDistributionInvestors = async () =>{
const handleDistributionInvestors = async () => {
try {
const res = await updateTransaction(id)
if (res?.data) {
const res = await updateTransaction(id);
if (res?.data) {
// toast({
// render: () => (
// <ToastBox status={"success"} message={res?.data?.message} />
// ),
// });
// setIsLoading(false);
onDistInvestorOpen()
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
onDistInvestorOpen();
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
// setIsLoading(false);
}
} catch (error) {
}
} catch (error) {
console.log(error);
}
}
};
const handleExit = async () =>{
const handleExit = async () => {
try {
const res = await updateTransaction(id)
if (res?.data) {
const res = await updateTransaction(id);
if (res?.data) {
// toast({
// render: () => (
// <ToastBox status={"success"} message={res?.data?.message} />
// ),
// });
// setIsLoading(false);
onExitOpen()
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
onExitOpen();
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
// setIsLoading(false);
}
} catch (error) {
}
} catch (error) {
console.log(error);
}
}
};
const handleInvestment = async () =>{
const handleInvestment = async () => {
try {
const res = await updateTransaction(id)
if (res?.data) {
const res = await updateTransaction(id);
if (res?.data) {
// toast({
// render: () => (
// <ToastBox status={"success"} message={res?.data?.message} />
// ),
// });
// setIsLoading(false);
onInvestmentOpen()
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
onInvestmentOpen();
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
// setIsLoading(false);
}
} catch (error) {
}
} catch (error) {
console.log(error);
}
}
};
const menu = [
{
@@ -216,7 +203,7 @@ const ViewIOdataHeader = ({ data, isLoading }) => {
id: 6,
title: "Distribution To Investors",
onClickFunction: handleDistributionInvestors,
},
},
{
id: 5,
title: "Update IO NAV",
@@ -250,8 +237,8 @@ const ViewIOdataHeader = ({ data, isLoading }) => {
apiTransactionTitles?.includes(item.id)
);
const balanceAmount = IODetails?.goalAmount - IODetails?.totalAmtInvestmentInUSD
const balanceAmount =
IODetails?.goalAmount - IODetails?.totalAmtInvestmentInUSD;
return IODetails?.investmentNameEnglish ? (
<Box
@@ -260,19 +247,19 @@ const ViewIOdataHeader = ({ data, isLoading }) => {
justifyContent={"space-between"}
gap={8}
bg={
IODetails?.ioStatus?.statusAdmin === "Draft"
IODetails?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_DRAFT
? "#EDF2F7"
: IODetails?.ioStatus?.statusAdmin === "Processing"
: IODetails?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_PROCESSING
? "#FEFBBF"
: IODetails?.ioStatus?.statusAdmin === "Open"
: IODetails?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_OPEN
? "#BEE2F8"
: IODetails?.ioStatus?.statusAdmin === "Closed"
: IODetails?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_CLOSED
? "#C6F6D5"
: IODetails?.ioStatus?.statusAdmin === "Exited"
: IODetails?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_EXITED
? "#FED7D7"
: IODetails?.ioStatus?.statusAdmin === "Cancelled"
: IODetails?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_CANCELLED
? "#E9D8FD"
: IODetails?.ioStatus?.statusAdmin === "DeActivate"
: IODetails?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_DEACTIVATE
? "#E9D8FD"
: null
}
@@ -317,7 +304,6 @@ const ViewIOdataHeader = ({ data, isLoading }) => {
</Box>
<Box>
<Box display={"flex"} gap={2} pb={1}>
<Text
as={"span"}
@@ -335,7 +321,7 @@ const ViewIOdataHeader = ({ data, isLoading }) => {
</Text>
</Box>
<Box display={"flex"} gap={2} pb={1}>
<Box display={"flex"} gap={2} pb={1}>
<Text
as={"span"}
fontSize={"xs"}
@@ -352,7 +338,7 @@ const ViewIOdataHeader = ({ data, isLoading }) => {
</Text>
</Box>
<Box display={"flex"} gap={2} pb={1}>
<Box display={"flex"} gap={2} pb={1}>
<Text
as={"span"}
fontSize={"xs"}
@@ -363,18 +349,13 @@ const ViewIOdataHeader = ({ data, isLoading }) => {
IO ID :-
</Text>
<Text as={"span"} fontSize={"xs"} fontWeight={"500"}>
{IODetails?.io_id
? IODetails?.io_id
: "---"}
{IODetails?.io_id ? IODetails?.io_id : "---"}
</Text>
</Box>
</Box>
</HStack>
<Box gap={8} me={12} w={"220px"}>
<Box gap={8} me={12} w={"220px"}>
<Box display={"flex"} justifyContent={"space-between"} gap={2} pb={1}>
<Text
as={"span"}
@@ -404,10 +385,13 @@ const ViewIOdataHeader = ({ data, isLoading }) => {
</Text>
<Text as={"span"} fontSize={"xs"} fontWeight={"500"}>
{/* {IODetails?.ioCash ? formatCurrency(removeTrailingZeros(IODetails?.ioCash)) : "00.00"} */}
{parseFloat(IODetails?.totalAmtInvestmentInUSD || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
{parseFloat(IODetails?.totalAmtInvestmentInUSD || 0).toLocaleString(
undefined,
{
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}
)}
</Text>
</Box>
@@ -448,17 +432,17 @@ const ViewIOdataHeader = ({ data, isLoading }) => {
textTransform={"none"}
// variant={"solid"}
colorScheme={
IODetails?.ioStatus?.statusAdmin === "Draft"
IODetails?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_DRAFT
? "gray"
: IODetails?.ioStatus?.statusAdmin === "Processing"
: IODetails?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_PROCESSING
? "yellow"
: IODetails?.ioStatus?.statusAdmin === "Open"
: IODetails?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_OPEN
? "blue"
: IODetails?.ioStatus?.statusAdmin === "Closed"
: IODetails?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_CLOSED
? "green"
: IODetails?.ioStatus?.statusAdmin === "Exited"
: IODetails?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_EXITED
? "red"
: IODetails?.ioStatus?.statusAdmin === "Cancelled"
: IODetails?.ioStatus?.statusAdmin === import.meta.env.VITE_STATUS_CANCELLED
? "purple"
: "purple"
}
@@ -532,39 +516,41 @@ const ViewIOdataHeader = ({ data, isLoading }) => {
alignItems={"start"}
height={"95px"}
>
{localStorage?.getItem("role") === "Maker" && <Menu>
<MenuButton
className="link p-1 rounded-1 "
bg={"#fff"}
_hover={{ backgroundColor: "#fff !important" }}
onClick={onOpen}
ref={btnRef}
>
<HiDotsVertical className="rubix-text-dark fs-6" />
</MenuButton>
<MenuList fontSize={"sm"}>
<MenuItem
_hover={{
bg: "#fff",
}}
as={"span"}
fontWeight={600}
className="border-bottom"
{isMaker() && (
<Menu>
<MenuButton
className="link p-1 rounded-1 "
bg={"#fff"}
_hover={{ backgroundColor: "#fff !important" }}
onClick={onOpen}
ref={btnRef}
>
Tansaction
</MenuItem>
{filteredMenu?.map(({ id, title, onClickFunction }) => (
<HiDotsVertical className="rubix-text-dark fs-6" />
</MenuButton>
<MenuList fontSize={"sm"}>
<MenuItem
key={id}
onClick={onClickFunction}
_hover={{
bg: "#fff",
}}
as={"span"}
fontWeight={600}
className="border-bottom"
>
{title}
Tansaction
</MenuItem>
))}
</MenuList>
</Menu>}
{filteredMenu?.map(({ id, title, onClickFunction }) => (
<MenuItem
key={id}
onClick={onClickFunction}
className="border-bottom"
>
{title}
</MenuItem>
))}
</MenuList>
</Menu>
)}
{/* Modals */}
<AmountInvested isOpen={isInvestmentOpen} onClose={onInvestmentClose} />

View File

@@ -203,7 +203,7 @@ const BankDetails = () => {
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<HStack>
<Text as={'span'} fontSize={'sm'} fontWeight={700}>Bank Deatils</Text>
<Text as={'span'} fontSize={'sm'} fontWeight={700}>Bank Details</Text>
</HStack>
<HStack
display={"flex"}

View File

@@ -1,5 +1,5 @@
import { ViewIcon } from "@chakra-ui/icons";
import {
Avatar,
Badge,
Box,
Button,
@@ -11,23 +11,21 @@ import {
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState, useRef } from "react";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import NormalTable from "../../../Components/DataTable/NormalTable";
import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
import {
ViewIcon,
} from "@chakra-ui/icons";
import Pagination from "../../../Components/Pagination";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import ToastBox from "../../../Components/ToastBox";
import { debounce } from "../../Master/Sponser/AddSponser";
import InvestmentDetailsEdit from "./InvestmentDetailsEdit";
import { useGetInvestorsQuery } from "../../../Services/investor.details.service";
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
import { exportToExcel, exportToExcelNew, generateSerialNumber } from "../../../Constants/Constants";
import React, { useContext, useEffect, useRef, useState } from "react";
import { LuFileSpreadsheet } from "react-icons/lu";
import { useNavigate } from "react-router-dom";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import NormalTable from "../../../Components/DataTable/NormalTable";
import Pagination from "../../../Components/Pagination";
import {
exportToExcelNew,
generateSerialNumber,
} from "../../../Constants/Constants";
import { INVESTOR_TABLE_PAGINATION } from "../../../Constants/Paginations";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import { useGetInvestorsQuery } from "../../../Services/investor.details.service";
import InvestmentDetailsEdit from "./InvestmentDetailsEdit";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
@@ -49,10 +47,11 @@ const InvestorDetails = () => {
} = useDisclosure();
const btnRef = React.useRef();
// =========================== [Use State] =============================
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
// =========================== [Use State] =============================
const [pageSize, setPageSize] = useState(INVESTOR_TABLE_PAGINATION?.size);
const [currentPage, setCurrentPage] = useState(
INVESTOR_TABLE_PAGINATION?.page
);
const [searchTerm, setSearchTerm] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
@@ -73,20 +72,19 @@ const InvestorDetails = () => {
data: investorDetails,
isLoading: investorDetailsLoading,
error,
} = useGetInvestorsQuery({
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search userStatus KYCStatus investorType_xid
search: debouncedSearchTerm,
userStatus: status,
KYCStatus: kyc,
country_xid: country
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
}
);
} = useGetInvestorsQuery(
{
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search userStatus KYCStatus investorType_xid
search: debouncedSearchTerm,
userStatus: status,
KYCStatus: kyc,
country_xid: country,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
}
);
useEffect(() => {
// Simulate loading
@@ -104,43 +102,45 @@ const InvestorDetails = () => {
"Client ID",
"First Name",
"Last Name",
"Country",
"Country",
"Phone Number",
"E-mail ID",
"Type",
// "Type",
"Wallet Balance",
"Investor Portfolio",
"KYC Status",
"Status",
// "Status",
"Action",
];
// ====================================================[Table Filter]================================================================
const exportInvestor = investorDetails?.data?.rows?.map((item, idx) => ({
"Id": parseInt(item?.id, 10) || item?.id, // Convert to integer, fallback to string if conversion fails
const exportInvestor = investorDetails?.data?.rows?.map((item) => ({
Id: parseInt(item?.id, 10) || item?.id, // Convert to integer, fallback to string if conversion fails
"Client ID": item?.clientReference_id, // This is likely a string
"First Name": item?.principal?.firstName,
"Last Name": item?.principal?.lastName,
"Country": item?.country?.countryName,
Country: item?.country?.countryName,
"Phone Number": item?.principal?.mobileNumber, // Skipping integer conversion, as this is likely a string
"E-mail ID": item?.principal?.emailAddress,
"Type": item?.investor_type?.investorTypeName,
"Status": item.ioStatus ? "Ban" : "Unban",
"KYC Status": item.KYCStatus ? "Completed" : "Not complete"
"Wallet Balance": item?.principal?.WalletBalance_InInvCur, // Skipping integer conversion, as this is likely a string
"Investor Portfolio": item?.principal?.Portfolio_InInvCur,
// Type: item?.investor_type?.investorTypeName,
// Status: item.ioStatus ? "Ban" : "Unban",
"KYC Status": item.KYCStatus ? "Completed" : "Not complete",
}));
const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({
id: item?.id,
"Sr No": (
<Text
w={'24px'}
w={"24px"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center fw-bold web-text-small"
>
{/* {item.id} */}
{generateSerialNumber(idx,currentPage, pageSize )}
{generateSerialNumber(idx, currentPage, pageSize)}
</Text>
),
"Client ID": (
@@ -148,7 +148,7 @@ const InvestorDetails = () => {
<Text as={"span"} color={"teal.900"}>
{item.clientReference_id}
</Text>
</Box>
</Box>
),
"First Name": (
<Box w={"auto"} isTruncated={true}>
@@ -185,42 +185,87 @@ const InvestorDetails = () => {
</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}
// 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>
// ),
// Status: (
// <Box w={"auto"} isTruncated={true}>
// <Badge
// fontWeight={"700"}
// textTransform={"none"}
// colorScheme={item.ioStatus ? "red" : "green"}
// px={2}
// py={0.5}
// >
// {item.ioStatus ? "Ban" : "Unban"}
// </Badge>
// </Box>
// ),
"Wallet Balance": (
<Box
display={"flex"}
justifyContent={"end"}
w={"130px"}
isTruncated={true}
>
<Text as={"span"} color={"teal.900"}>
{/* {formatCurrency(removeTrailingZeros(item?.investorAmount))} */}
{parseFloat(item?.WalletBalance_InInvCur || 0).toLocaleString(
undefined,
{
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}
)}
<Badge ms={1} colorScheme="green">
{item?.currencyCode}
</Badge>
</Text>
</Box>
),
Status: (
<Box w={"auto"} isTruncated={true}>
<Badge
fontWeight={"700"}
textTransform={"none"}
colorScheme={item.ioStatus ? "red" : "green"}
px={2}
py={0.5}
"Investor Portfolio": (
<Box
display={"flex"}
justifyContent={"end"}
w={"130px"}
isTruncated={true}
>
{item.ioStatus ? "Ban" : "Unban"}
</Badge>
</Box>
<Text as={"span"} color={"teal.900"}>
{parseFloat(item?.Portfolio_InInvCur || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
<Badge ms={1} colorScheme="green">
{item?.currencyCode}
</Badge>
</Text>
</Box>
),
"KYC Status": (
<Box w={"auto"} display={'flex'} alignItems={'center'} isTruncated={true}>
<Box w={"auto"} display={"flex"} alignItems={"center"} isTruncated={true}>
<Text
as={'span'}
as={"span"}
fontWeight={"700"}
textTransform={"none"}
color={item?.KYCStatus === true ? "green" : "yellow.500"}
px={2}
py={0.5}
variant={'solid'}
variant={"solid"}
>
{/* {item.KYCStatus ? "Completed" : "Not complete"} */}
{item?.KYCStatus === true ? "Completed" : "NotCompleted"}
{item?.KYCStatus === true ? "Completed" : "Not Completed"}
</Text>
</Box>
),
@@ -235,7 +280,7 @@ const InvestorDetails = () => {
placement="top"
>
<Button
isDisabled={item.ioStatus}
isDisabled={item.ioStatus}
onClick={() => {
navigate(`/investor-details/profile-view/${item.id}`);
}}
@@ -266,7 +311,6 @@ const InvestorDetails = () => {
setIsLoading(true);
};
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<Box bg="white.500">
@@ -321,7 +365,7 @@ const InvestorDetails = () => {
KYC Status
</option>
<option value="">KYC Status</option>
<option value="0">Incompleted</option>
<option value="0">Not Completed</option>
<option value="1">Completed</option>
</Select>
@@ -337,7 +381,7 @@ const InvestorDetails = () => {
Country
</option>
<option value="">All</option>
<option value="1">Behrain</option>
<option value="1">Bahrain</option>
<option value="2">Kuwait</option>
<option value="3">Oman</option>
<option value="4">Qatar</option>
@@ -346,35 +390,29 @@ const InvestorDetails = () => {
</Select>
<Pagination
isLoading={investorDetailsLoading}
isLoading={investorDetailsLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={investorDetails?.data?.totalItems}
/>
</HStack>
<Button
onClick={() =>
exportToExcelNew(exportInvestor, "Investor Details")
}
leftIcon={<LuFileSpreadsheet />}
colorScheme="forestGreen"
size={"sm"}
variant={"outline"}
rounded={"sm"}
fontSize={"xs"}
w={100}
me={2}
isDisabled={exportInvestor?.length === 0}
>
Export xls
</Button>
onClick={() => exportToExcelNew(exportInvestor, "Investor Details")}
leftIcon={<LuFileSpreadsheet />}
colorScheme="forestGreen"
size={"sm"}
variant={"outline"}
rounded={"sm"}
fontSize={"xs"}
w={100}
me={2}
isDisabled={exportInvestor?.length === 0}
>
Export xls
</Button>
</HStack>
<InvestmentDetailsEdit
id={actionId}

View File

@@ -150,6 +150,22 @@ const Kyc = () => {
/>
</FormControl>
</HStack>
<HStack spacing={4} mb={4}>
<FormControl>
<FormLabel mb={1} fontSize={"sm"}>
PEP Status
</FormLabel>
<Input
bg={"#ccc3"}
border={"none"}
size={"sm"}
value={data?.data?.KYC?.PEPStatus ? "Yes" : "No"}
type="text"
readOnly
/>
</FormControl>
<FormControl></FormControl>
</HStack>
{/* <HStack spacing={4}>
<FormControl>
<FormLabel mb={1} fontSize={"sm"}>Address</FormLabel>

View File

@@ -3,7 +3,7 @@ import Input01 from "../Components/Inputs/Input01";
import logo from "../assets/logo2.png";
import { useDispatch, useSelector } from "react-redux";
import { loginUser } from "../Redux/Slice/auth";
import { useContext, useEffect, useState } from "react";
import { useContext, useEffect, useRef, useState } from "react";
import Button01 from "../Components/Buttons/Button01";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
@@ -12,12 +12,15 @@ import Loader01 from "../Components/Loaders/Loader01";
import Asset1 from "../assets/asset1.png";
import Asset2 from "../assets/asset2.png";
import {
Box,
Button,
FormControl,
FormLabel,
Input,
InputGroup,
InputRightElement,
Text,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import GlobalStateContext from "../Contexts/GlobalStateContext";
@@ -28,6 +31,7 @@ import { useLoginMutation } from "../Services/token.serivce";
// import { yupResolver } from "@hookform/resolvers/yup";
import * as Yup from "yup";
import ForgetPassword from "./ForgetPassword";
const validationSchema = Yup.object().shape({
@@ -50,6 +54,8 @@ const Login = () => {
const dispatch = useDispatch();
const [login] = useLoginMutation()
const { isOpen, onOpen, onClose } = useDisclosure();
const firstField = useRef();
useEffect(() => {
@@ -72,7 +78,7 @@ const Login = () => {
setIsLoading(false);
setIsAuthenticate(true);
Cookies.set("isAuthenticated", true, { expires: 7 });
navigate("/sponser");
navigate("/");
reset();
}
@@ -205,7 +211,7 @@ const Login = () => {
)}
</FormControl>
<FormControl className="mb-4">
<FormControl className="mb-2">
<FormLabel className="rubix-text-dark ps-1 web-text-medium fw-bold">
Password <span className="text-danger">*</span>
</FormLabel>
@@ -238,6 +244,9 @@ const Login = () => {
</span>
)}
</FormControl>
<Box fontSize={"sm"} display={"flex"} justifyContent={"end"} mt={0}>
<Text fontWeight={500} cursor={"pointer"} onClick={onOpen}>Forgot Password?</Text>
</Box>
<Button
isLoading={isLoading}
@@ -247,6 +256,8 @@ const Login = () => {
color={"whitesmoke"}
colorScheme="green.500"
size="lg"
fontWeight={500}
fontSize={"md"}
>
Log In
</Button>
@@ -317,6 +328,11 @@ const Login = () => {
src={Asset2}
alt="bg-img"
/>
<ForgetPassword
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
</div>
);
};

View File

@@ -30,8 +30,37 @@ import {
} from "../../../Services/exchange.rate.service";
import ToastBox from "../../../Components/ToastBox";
import { getTomorrowDate } from "../../../Constants/Constants";
import * as yup from "yup";
import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
// const editExchange = yup.object().shape({
// rate: yup
// .number()
// .required("Rate is required")
// .positive("Rate must be greater than 0")
// .test(
// "is-decimal",
// "Rate must have at most 8 decimal places",
// (value) =>
// value !== undefined && value.toString().match(/^\d+(\.\d{1,8})?$/)
// ),
// });
const editExchange = yup.object().shape({
rate: yup
.string()
.required("Rate is required")
.matches(
/^\d+\.\d{8}$/,
"Rate must have exactly 8 decimal places"
)
.test(
"is-positive",
"Rate must be greater than 0",
(value) => parseFloat(value) > 0
),
});
// Convert date to YYYY-MM-DD format
const formatDateValue = (date) => {
if (!date) return "";
@@ -57,8 +86,9 @@ const EditExchangeRate = ({
const toast = useToast();
const {} = useDisclosure();
const [isBtnLoading, setIsBtnLoading] = useState(false);
const [rateError, setRateError] = useState("");
const { data, isLoading, errors } = useGetExchangeRateByIdQuery(id, {
const { data, isLoading, errors,refetch, isFetching } = useGetExchangeRateByIdQuery(id, {
skip: !id,
});
@@ -67,17 +97,45 @@ const EditExchangeRate = ({
const [rate, setRate] = useState("");
const [alert, setAlert] = useState(false);
console.log(rate);
useEffect(() => {
if (id) {refetch()}
if (foundObject) {
setRate(foundObject.rate);
const numericRate = parseFloat(foundObject.rate) || 0; // Convert to number or default to 0 if invalid
setRate(numericRate.toFixed(8)); // Set rate with exactly 8 decimal places
}
}, [foundObject]);
}, [foundObject, isOpen]);
// useEffect(()=>{
// if (id) {
// refetch()
// }
// },[isOpen])
const validateRate = async () => {
try {
await editExchange.validate({ rate });
setRateError(""); // Clear validation error if valid
return true;
} catch (error) {
setRateError(error.message); // Display validation error
return false;
}
};
const handleSave = async () => {
const isValid = await validateRate();
if (!isValid) {
return; // Prevent submission if validation fails
}
setIsBtnLoading(true);
try {
const data = {
rate: rate,
rate,
};
const res = await updateExchange({ data, id });
if (res?.data?.statusCode === 200) {
@@ -88,9 +146,31 @@ const EditExchangeRate = ({
setAlert(false);
onClose();
}
} catch (error) {}
} catch (error) {
setIsBtnLoading(false);
// Handle error
}
};
const checkValidate = async (e) => {
e.preventDefault();
// Wait for the validation to complete
const isValid = await validateRate();
if (!isValid) {
return; // Prevent submission if validation fails
} else {
setAlert(true); // Only trigger modal if validation passes
}
};
useEffect(() => {
if (rate) {
validateRate();
}
}, [rate]);
return (
<>
<Drawer
@@ -100,18 +180,13 @@ const EditExchangeRate = ({
onClose={onClose}
finalFocusRef={btnRef}
>
<form
onSubmit={(e) => {
e.preventDefault();
setAlert(true);
}}
>
<form onSubmit={(e) => checkValidate(e)}>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader fontSize={"md"}>Edit rate</DrawerHeader>
{isLoading ? (
{isFetching ? (
<FullscreenLoaders />
) : (
<>
@@ -153,16 +228,26 @@ const EditExchangeRate = ({
<Text fontSize={"sm"}>{formatDate(getTomorrowDate())}</Text>
</FormControl>
<FormControl mb={4} isRequired>
<FormControl mb={4} isRequired isInvalid={!!rateError}>
<FormLabel fontSize={"sm"}>Rate</FormLabel>
<Input
required
type="number"
placeholder="Type rate here..."
size={"sm"}
value={rate}
onChange={(e) => setRate(e.target.value)}
onChange={(e) => {
const value = e.target.value;
// Match numbers with at most 8 decimal places
if (/^\d*\.?\d{0,8}$/.test(value)) {
setRate(value);
}
}}
/>
{rateError && (
<Text color="red.500" fontSize="sm" mt={1}>
{rateError}
</Text>
)}
</FormControl>
</DrawerBody>
<DrawerFooter>
@@ -173,6 +258,15 @@ const EditExchangeRate = ({
size={"sm"}
mr={3}
onClick={onClose}
// onClick={() => {
// window.location.reload();
// onClose();
// }}
// onClick={() => {
// setRate("");
// setRateError("");
// onClose();
// }}
>
Cancel
</Button>

View File

@@ -316,7 +316,7 @@ const AddInvestmentType = () => {
<Box display={"flex"} justifyContent={"space-between"} alignItems={"center"} mt={5} px={4} mb={5}>
<Text fontSize={"sm"} mb={0} onClick={() => navigate(-1)} cursor={"pointer"}>
<ArrowBackIcon fontSize={"xl"} me={2} />Add Details
<ArrowBackIcon fontSize={"xl"} me={2} />{params?.id ? "Edit Details" : "Add Details"}
</Text>
<SwitchButton isSwitchOn={isSwitchOn} setIsSwitchOn={setIsSwitchOn} />
</Box>

View File

@@ -254,7 +254,7 @@ const AddSponser = () => {
} characters.`,
},
{
label: "Email adress",
label: "Email address",
name: "email",
placeHolder: " ",
type: "email",
@@ -301,7 +301,7 @@ const AddSponser = () => {
{/* ===================== [Switch Button] ======================== */}
<Box display={"flex"} justifyContent={"space-between"} alignItems={"center"} mt={5} px={4}>
<Text fontSize={"sm"} mb={0} onClick={() => navigate(-1)} cursor={"pointer"}>
<ArrowBackIcon fontSize={"xl"} me={2} />Add Details
<ArrowBackIcon fontSize={"xl"} me={2} />{params?.id ? "Edit Details" : "Add Details"}
</Text>
<SwitchButton isSwitchOn={isSwitchOn} setIsSwitchOn={setIsSwitchOn} />
</Box>

View File

@@ -1,26 +1,21 @@
import { CheckIcon, CloseIcon, InfoIcon } from "@chakra-ui/icons";
import {
Avatar,
Box,
ButtonGroup,
Editable,
EditableInput,
EditablePreview,
EditableTextarea,
Flex,
HStack,
Heading,
Icon,
IconButton,
Input,
Text,
VStack,
useEditableControls,
useEditableControls
} from "@chakra-ui/react";
import React from "react";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import { CheckIcon, CloseIcon, EditIcon, InfoIcon } from "@chakra-ui/icons";
import React, { useEffect, useState } from "react";
import { FaEarthAmericas } from "react-icons/fa6";
import logoMini from "../../assets/propic.png";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import { useAuthProfileQuery } from "../../Services/token.serivce";
const Profile = () => {
/* Here's a custom control */
@@ -53,19 +48,56 @@ const Profile = () => {
)
);
}
const { data } = useAuthProfileQuery();
// Array of fields to render
const fields = [
{ name: "firstName", label: "First Name", defaultValue: "Faisal" },
{ name: "lastName", label: "Last Name", defaultValue: "Aljalahma" },
{ name: "email", label: "Email Address", defaultValue: "f.aljalahma@tanamicapital.com" },
{ name: "mobile", label: "Mobile Number", defaultValue: "9898767876" },
{ name: "role", label: "Role", defaultValue: "Maker" },
];
const [fields, setFields] = useState([
{
name: "firstName",
label: "First Name",
defaultValue: null,
},
{
name: "lastName",
label: "Last Name",
defaultValue: null,
},
{
name: "email",
label: "Email Address",
defaultValue: null,
},
{
name: "mobile",
label: "Mobile Number",
defaultValue: null,
},
{ name: "role", label: "Role", defaultValue: null },
]);
useEffect(() => {
setFields([
{
name: "firstName",
label: "First Name",
defaultValue: data?.data?.firstName || null,
},
{
name: "lastName",
label: "Last Name",
defaultValue: data?.data?.lastName || null,
},
{
name: "email",
label: "Email Address",
defaultValue: data?.data?.emailAddress || null,
},
{
name: "mobile",
label: "Mobile Number",
defaultValue: data?.data?.mobileNumber || null,
},
{ name: "role", label: "Role", defaultValue: data?.data?.role || null },
]);
}, [data]);
return (
<VStack
@@ -114,7 +146,7 @@ const Profile = () => {
color={"gray.700"}
fontWeight={500}
>
Faisal Aljalahma
{data?.data?.firstName + " " + data?.data?.lastName}
</Text>
<Text
@@ -123,7 +155,7 @@ const Profile = () => {
color={"gray.500"}
fontWeight={400}
>
f.aljalahma@tanamicapital.com
{data?.data?.emailAddress}
</Text>
</VStack>
</HStack>
@@ -150,16 +182,16 @@ const Profile = () => {
fontWeight={500}
>
{" "}
<Icon as={FaEarthAmericas} /> Maker
<Icon as={FaEarthAmericas} /> {data?.data?.role}
</Text>
</VStack>
</HStack>
</Box>
{/*
<Heading as="h3" size="sm">
About you
</Heading>
<Box
rounded="md"
boxShadow="base"
@@ -170,55 +202,56 @@ const Profile = () => {
alignItems="flex-start"
p={6}
gap={0}
pb={6}
>
{fields?.map((item) => (
<VStack alignItems={"flex-start"} w={"100%"} gap={1.5} mb={6} key={item?.label}>
<Text
as={"span"}
fontSize="xs"
fontWeight="semibold"
color={"gray.500"}
>
{item?.label}
</Text>
<Editable
position={"relative"}
gap={0}
defaultValue={item?.defaultValue}
w="100%"
>
<EditablePreview
cursor={'pointer'}
p={2}
rounded={"sm"}
w={"100%"}
_hover={{
bg: "gray.100",
}}
fontSize="sm"
transition={"0.5s"}
/>
<Input
as={EditableInput}
ps={2}
size={'sm'}
fontSize="sm"
rounded={"sm"}
_focus={{
borderColor:"blue.500"
}}
/>
<EditableControls />
</Editable>
</VStack>
))}
</Box>
{fields?.map((item) => (
<VStack
alignItems={"flex-start"}
w={"100%"}
gap={1.5}
mb={6}
key={item?.label}
>
<Text
as={"span"}
fontSize="xs"
fontWeight="semibold"
color={"gray.500"}
>
{item?.label}
</Text>
<Editable
position={"relative"}
gap={0}
defaultValue={item?.defaultValue}
w="100%"
>
<EditablePreview
cursor={"pointer"}
p={2}
rounded={"sm"}
w={"100%"}
_hover={{
bg: "gray.100",
}}
fontSize="sm"
transition={"0.5s"}
/>
<Input
as={EditableInput}
ps={2}
size={"sm"}
fontSize="sm"
rounded={"sm"}
_focus={{
borderColor: "blue.500",
}}
/>
<EditableControls />
</Editable>
</VStack>
))}
</Box> */}
</VStack>
</VStack>
);

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,98 @@
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 RejectModal = ({ isOpen, onClose, firstField }) => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const onSubmit = (data) => {
setFile(data.document[0]);
const newDocument = {
...data,
document: data.document[0].name, // Store the document name
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} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea rows={6}
focusBorderColor='green.400'
name="fileName"
{...register("fileName")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comments...."}
rounded={'md'}
resize={'none'}
/>
{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 RejectModal;

View File

@@ -0,0 +1,404 @@
import { CheckIcon, CloseIcon } from "@chakra-ui/icons";
import {
Box,
Button,
HStack,
Input,
Text,
Tooltip,
useBoolean,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
import CustomAlertDialog from "../../Components/CustomAlertDialog";
import NormalTable from "../../Components/DataTable/NormalTable";
import { TABLE_PAGINATION } from "../../Constants/Paginations";
import GlobalStateContext from "../../Contexts/GlobalStateContext";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import {
useApproveAccountDeletionRequestMutation,
useGetAccountDeletionMasterQuery,
useRejectAccountDeletionRequestMutation,
} from "../../Services/reversal.account.deletion.service";
import ConfirmModal from "./ConfirmModal";
import RejectModal from "./RejectModal";
import RejectReversalPopups from "../../Components/Popups/RejectReversalPopups";
import ToastBox from "../../Components/ToastBox";
import ConfirmReversalPopups from "../../Components/Popups/ConfirmReversalPopups";
// import { formatDate } from "../../Components/Functions/UTCConvertor";
const ReversalAccountDeletion = () => {
const toast = useToast();
const { slideFromRight, setDeleteHistory } = useContext(GlobalStateContext);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
// =========================== [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 [isReversalLoading, setIsReversalLoading] = useBoolean();
const {
isOpen: isConfirmOpen,
onOpen: onConfirmOpen,
onClose: onConfirmClose,
} = useDisclosure();
const {
isOpen: isRejectOpen,
onOpen: onRejectOpen,
onClose: onRejectClose,
} = useDisclosure();
const [rejectAccountDeletionRequest] = useRejectAccountDeletionRequestMutation();
const [approveAccountDeletionRequest] = useApproveAccountDeletionRequestMutation();
// 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: deleteHistory,
isLoading,
refetch,
} = useGetAccountDeletionMasterQuery(
{
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
search: debouncedSearchTerm,
},
{
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
}
);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
// Use useEffect to refetch data when the component mounts
useEffect(() => {
refetch();
}, [refetch]);
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr No.",
"Request Date",
"Client ID",
"First Name",
"Last Name",
"Phone Number",
"Country",
"Action",
];
const extractedArray = deleteHistory?.data?.rows?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.800"}
className="d-flex align-items-center web-text-small"
fontWeight={"500"}
>
{index + 1}.
</Text>
),
"Request Date": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center web-text-small"
fontWeight={"500"}
>
{formatDate(item.isReversalDate)}
</Text>
),
"Client ID": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center web-text-small"
fontWeight={"500"}
>
{item?.iamPrincipal?.investor_details?.clientReference_id}
</Text>
),
"First Name": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.800"}
className="d-flex align-items-center web-text-small"
fontWeight={"500"}
>
{item?.iamPrincipal?.firstName}
{/* {formatDate(item.charge)} */}
</Text>
),
"Last Name": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.800"}
className="d-flex align-items-center web-text-small"
fontWeight={"500"}
>
{item?.iamPrincipal?.lastName}
</Text>
),
"Phone Number": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center web-text-small"
fontWeight={"500"}
>
{item?.iamPrincipal?.ISDcode + " " + item?.iamPrincipal?.mobileNumber}
</Text>
),
Country: (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center web-text-small"
fontWeight={"500"}
>
{item?.iamPrincipal?.investor_details?.country?.countryName}
</Text>
),
Action: (
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="Approve"
bg="#fff"
color={"green.500"}
placement="left-start"
>
<Button
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 deleteHistory = sponser.filter((sponsor) => sponsor.id !== actionId);
setTimeout(() => {
setSponser(deleteHistory);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const handleApproved = async (data) => {
setIsReversalLoading.on(); // Start loading
try {
const { error, data: responseData } = await rejectAccountDeletionRequest({
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 approveAccountDeletionRequest({
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}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
ps={1}
pe={1}
pb={4}
pt={4}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
{/* <HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} />
</HStack> */}
</HStack>
</Box>
<NormalTable
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
// 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}
/>
<ConfirmReversalPopups
isOpen={isConfirmOpen}
onClose={onConfirmClose}
handleConfirm={handleConfirm}
isLoading={isReversalLoading}
/>
<RejectReversalPopups
isOpen={isRejectOpen}
onClose={onRejectClose}
handelApproved={handleApproved}
isLoading={isReversalLoading}
/>
</Box>
);
};
export default ReversalAccountDeletion;

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,98 @@
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 RejectModal = ({ isOpen, onClose, firstField }) => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(conformModalSchema),
});
const onSubmit = (data) => {
setFile(data.document[0]);
const newDocument = {
...data,
document: data.document[0].name, // Store the document name
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} isRequired>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea rows={6}
focusBorderColor='green.400'
name="fileName"
{...register("fileName")}
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comments...."}
rounded={'md'}
resize={'none'}
/>
{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 RejectModal;

View File

@@ -0,0 +1,427 @@
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 NormalTable from "../../Components/DataTable/NormalTable";
import { generateSerialNumber } from "../../Constants/Constants";
import { TABLE_PAGINATION } from "../../Constants/Paginations";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import { useGetFawateerForMakerRequestQuery } from "../../Services/fawateer.request.service";
import ConfirmModal from "./ConfirmModal";
import RejectModal from "./RejectModal";
import {
useApproveFawateerRequestMutation,
useGetFawateerDepositMasterQuery,
useRejectFawateerRequestMutation,
} from "../../Services/reversal.fawateer.deposit.service";
import RejectReversalPopups from "../../Components/Popups/RejectReversalPopups";
import ToastBox from "../../Components/ToastBox";
import ConfirmReversalPopups from "../../Components/Popups/ConfirmReversalPopups";
const ReversalFawateerDeposit = () => {
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 [isReversalLoading, setIsReversalLoading] = useBoolean();
const [rejectFawateerRequest] = useRejectFawateerRequestMutation();
const [approveFawateerRequest] = useApproveFawateerRequestMutation();
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,
} = useGetFawateerDepositMasterQuery(
{
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(() => {
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.principal?.firstName;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches;
});
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr.no",
"Request Date",
"Client ID",
"First Name",
"Last Name",
"Phone Number",
"Deposit Amount (BHD)",
"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>
),
"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?.transaction_details?.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?.principal?.investor_details?.clientReference_id}
</Text>
),
"First Name": (
<Box isTruncated={true} w={"80px"}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.principal?.firstName}
</Text>
</Box>
),
"Last Name": (
<Box isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{item?.principal?.lastName}
</Text>
</Box>
),
"Phone Number": (
<Box w={"100px"} isTruncated={true}>
<Text as={"span"} color={"teal.900"}>
{/* {item?.principal?.ISDcode} {item?.principal?.mobileNumber} */}
{item?.principal?.ISDcode + " " + item?.principal?.mobileNumber}
</Text>
</Box>
),
"Deposit Amount (BHD)": (
<Box isTruncated={true}>
<Text as={"span"} color={"teal.900"} textAlign={"right"}>
{parseFloat(item?.transaction_details?.investorAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
<Badge ms={1} colorScheme="green">
{item?.currencyCode}
</Badge>
</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 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 rejectFawateerRequest({
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 approveFawateerRequest({
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}>
<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}
/>
<ConfirmReversalPopups
isOpen={isConfirmOpen}
onClose={onConfirmClose}
handleConfirm={handleConfirm}
isLoading={isReversalLoading}
/>
<RejectReversalPopups
isOpen={isRejectOpen}
onClose={onRejectClose}
handelApproved={handleApproved}
isLoading={isReversalLoading}
/>
</Box>
);
};
export default ReversalFawateerDeposit;

View File

@@ -0,0 +1,373 @@
import { AddIcon, DeleteIcon, EditIcon } from "@chakra-ui/icons";
import {
Badge,
Box,
Button,
HStack,
Input,
Switch,
Text,
Tooltip,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import CustomAlertDialog from "../../Components/CustomAlertDialog";
import NormalTable from "../../Components/DataTable/NormalTable";
import ToastBox from "../../Components/ToastBox";
import {
CHECKER_ID,
generateSerialNumber,
MAKER_ID,
SUPER_ADMIN_ID,
} from "../../Constants/Constants";
import GlobalStateContext from "../../Contexts/GlobalStateContext";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import {
useDeleteUserMutation,
useGetSubAdminMasterQuery,
useToggleStatusMutation,
} from "../../Services/subadmin.service";
export const formatDate = (date) => {
const d = new Date(date);
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, "0"); // Months are 0-indexed
const day = String(d.getDate()).padStart(2, "0");
return `${day}/${month}/${year}`;
};
const SubAdmin = () => {
const navigate = useNavigate();
const toast = useToast();
const [isLoading, setIsLoading] = useState(false);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
// const [deleteSponser] = useDeleteSponserMutation();
const { slideFromRight } = useContext(GlobalStateContext);
// =========================== [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: subAdmin, isLoading: isSponserLoading } =
useGetSubAdminMasterQuery();
const [deleteUser] = useDeleteUserMutation();
const [toggleStatus] = useToggleStatusMutation();
// useEffect(() => {
// if (subAdmin?.data) {
// setIsSwitchOn(subAdmin?.role?.role);
// console.log(subAdmin);
// }
// }, [subAdmin]);
// ==============================[Table Filter]========================
const filteredData = subAdmin?.data?.filter((item) => {
const name = item.firstName;
const searchLower = searchTerm?.toLowerCase();
const nameMatches = name?.toLowerCase().includes(searchLower);
return nameMatches;
});
const handleToggleStatus = async (isMaker, id) => {
// console.log("hit");
const data = {
role_xid: isMaker ? CHECKER_ID : MAKER_ID,
};
console.log("=======================", data);
try {
const res = await toggleStatus({ id, data });
if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
} else if (res) {
toast({
render: () => (
<ToastBox
status={"success"}
message={res?.message || "Status updated successfully!"}
/>
),
});
}
} catch (error) {
toast({
render: () => (
<ToastBox
status={"error"}
message={error?.data?.message || "Something went wrong"}
/>
),
});
}
};
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr No",
"First Name",
"Last Name",
"Email Address",
"Role",
"Action",
];
const extractedArray = filteredData?.map((item, index) => ({
"Sr No": (
<Text
w={"24px"}
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"gray.600"}
className="d-flex align-items-center fw-bold web-text-small"
>
{/* {item.id} */}
{generateSerialNumber(index)}
</Text>
),
"First Name": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item?.firstName}
</Text>
),
"Last Name": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
color={"teal.900"}
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
{item?.lastName}
</Text>
),
"Email Address": (
<Box isTruncated={true}>
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
{item?.emailAddress}
</Text>
</Box>
),
Role: (
<Box isTruncated={true}>
<Badge
py={"2px"}
me={2}
fontWeight={600}
bg={item?.role[0]?.role === "Maker" ? "#00ffcc" : "#b3ff99"}
px={item?.role[0]?.role === "Maker" ? "12px" : "5px"}
>
{item?.role[0]?.role}
</Badge>
{/* <Switch
onChange={() =>
handleToggleStatus(item?.role[0]?.role === "Maker", item?.id)
}
isChecked={item?.role[0]?.role === "Maker"}
// colorScheme={item?.role[0]?.role === "Maker" ? "green" : "teal"}
sx={{
".chakra-switch__track": {
bg: item?.role[0]?.role === "Maker" ? "#00ffcc" : "#b3ff99", // "Off" state color
},
}}
/> */}
{/* <RoleSwitchButton
setIsSwitchOn={setIsSwitchOn}
isSwitchOn={item?.role[0]?.role === "Maker"}
onClick={() => handleToggleStatus(item?.role[0]?.role=== "Maker")}
/> */}
</Box>
),
Action: (
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="Edit"
bg="#fff"
color={"blue.500"}
placement="top"
>
<Button
onClick={() => navigate(`/subadmin/subadmin-update/${item.id}`)}
// _hover={{ color: "blue.500" }}
// color="blue.400"
rounded={"sm"}
size={"xs"}
colorScheme="blue"
>
<EditIcon />
</Button>
</Tooltip>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="Delete"
bg="#fff"
color={"red.500"}
placement="top"
>
<Button
isDisabled={item?.id === SUPER_ADMIN_ID}
onClick={() => {
setActionId(item?.id);
setDeleteAlert(true);
}}
// _hover={{ color: "red.500" }}
// color="red"
// disabled={true}
rounded={"sm"}
size={"xs"}
colorScheme="red"
variant={"solid"}
>
<DeleteIcon />
</Button>
</Tooltip>
</Box>
),
}));
// =========================== [ Delete Function ] =================================
const handleDelete = async () => {
setIsLoading(true);
try {
const response = await deleteUser(actionId);
if (response?.error?.data?.code === 400) {
toast({
render: () => (
<ToastBox
message={response?.error?.data?.message}
status={"error"}
/>
),
});
setIsLoading(false);
setDeleteAlert(false);
} else if (
response?.data?.statusCode === 201 ||
response?.data?.statusCode === 200
) {
toast({
render: () => (
<ToastBox message={response?.data?.message} status={"success"} />
),
});
setIsLoading(false);
setDeleteAlert(false);
}
} catch (error) {}
};
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"
>
{/* =======================[Search Input]======================== */}
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
{/* ====================[Pagination]===================== */}
{/* <Pagination
isLoading={isSponserLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={subAdmin?.data?.totalItems}
/> */}
{/* =====================[Add Button]===================== */}
<Link to={"/subadmin/subadmin-update"}>
<Button
leftIcon={<AddIcon />}
colorScheme={"forestGreen"}
rounded={"sm"}
fontSize={"xs"}
size={"sm"}
>
Add
</Button>
</Link>
</HStack>
</HStack>
</Box>
{/* =================== [Data Table] ===================== */}
<NormalTable
emptyMessage={`We don't have any Sponers `}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isSponserLoading}
viewActionId={actionId}
setViewActionId={setActionId}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
{/* ======================== [Modal] ======================== */}
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sub-admin?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
</Box>
);
};
export default SubAdmin;

View File

@@ -0,0 +1,354 @@
import { ArrowBackIcon } from "@chakra-ui/icons";
import { Box, Text, useToast } from "@chakra-ui/react";
import { yupResolver } from "@hookform/resolvers/yup";
import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { useNavigate, useParams } from "react-router-dom";
import * as yup from "yup";
import CustomAlertDialog from "../../Components/CustomAlertDialog";
import FormInputMain from "../../Components/FormInputMain";
import FullscreenLoaders from "../../Components/Loaders/FullscreenLoaders";
import RoleSwitchButton from "../../Components/RoleSwitchButton";
import ToastBox from "../../Components/ToastBox";
import {
isMaker
} from "../../Constants/Constants";
import { OPACITY_ON_LOAD } from "../../Layout/animations";
import {
useCreateSubAdminMutation,
useGetSubAdminByIdQuery,
useUpdateSubAdminMutation,
} from "../../Services/subadmin.service";
// ======================= [validation] =========================
const addSubAdminSchema = yup.object().shape({
firstName: yup
.string()
.required("First Name is required")
.min(3, "First Name must be at least 3 characters long")
.max(35, "First Name cannot exceed 35 characters")
.matches(/^[^\d]+$/, "First Name cannot contain numbers"),
lastName: yup.string().required("Last Name is required")
.min(3, "Last Name must be at least 3 characters long")
.max(35, "Last Name cannot exceed 35 characters")
.matches(/^[^\d]+$/, "Last Name cannot contain numbers"),
emailAddress:yup.
string()
.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"),
});
// ==================== [debounce] ========================
export function debounce(func, delay) {
let debounceTimer;
return function (...args) {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => func.apply(this, args), delay);
};
}
const SubAdminUpdateCreate = () => {
const toast = useToast();
const params = useParams();
const navigate = useNavigate();
const id = params?.id;
// =====================[useState]=======================
const [isLoadingBtn, setIsLoadingBtn] = useState(false);
const [alert, setAlert] = useState(false);
const [form, setForm] = useState();
const [isSwitchOn, setIsSwitchOn] = useState(false);
const [createSubAdmin] = useCreateSubAdminMutation();
const [updateSubAdmin] = useUpdateSubAdminMutation();
// Fetch sponsor data only if id exists
const { data: subAdminByIdData, isLoading } = useGetSubAdminByIdQuery(id, {
skip: !id,
});
// ======================== [validators] ===========================
const {
control,
watch,
handleSubmit,
formState: { errors },
reset,
} = useForm({
resolver: yupResolver(addSubAdminSchema),
mode: "all",
});
// ========================== [useEffect] ================================
useEffect(() => {
if (subAdminByIdData?.data) {
reset({
firstName: subAdminByIdData?.data?.firstName,
lastName: subAdminByIdData?.data?.lastName,
emailAddress: subAdminByIdData?.data?.emailAddress,
});
setIsSwitchOn(isMaker(subAdminByIdData?.data?.role[0]?.role));
}
}, [subAdminByIdData, reset]);
if (isLoading) {
return <FullscreenLoaders />;
}
// ============================ [API]===============================
const handleConfirm = async () => {
setIsLoadingBtn(true);
const id = params?.id;
console.log(isSwitchOn);
if (id) {
try {
const formData = {
...form,
role_xid: !isSwitchOn ? 2 : 1,
};
await updateSubAdmin({ data: formData, id }).then((response) => {
if (response?.data?.statusCode) {
toast({
render: () => <ToastBox message={response?.data?.message} />,
});
setIsLoadingBtn(false);
setAlert(false);
navigate("/subadmin");
} else if (response?.error?.status === 400) {
toast({
render: () => (
<ToastBox
message={response?.error?.data?.message}
status={"error"}
/>
),
});
setIsLoadingBtn(false);
setAlert(false);
}
});
} catch (error) {
console.log(error);
setIsLoadingBtn(false);
navigate("/subadmin");
}
} else {
try {
const formData = {
...form,
role_xid: isSwitchOn ? 1 : 2,
};
await createSubAdmin(formData).then((response) => {
console.log(response);
if (response?.data?.statusCode === 201) {
toast({
render: () => <ToastBox message={response?.data?.message} />,
});
setIsLoadingBtn(false);
navigate("/subadmin");
} else if (response?.error?.status === 400) {
toast({
render: () => (
<ToastBox
message={response?.error?.data?.message}
status={"error"}
/>
),
});
setIsLoadingBtn(false);
setAlert(false);
}
});
} catch (error) {
console.log(error);
setIsLoadingBtn(false);
navigate("/subadmin");
}
}
};
// ====================== [Update Form Object] =========================
const formFields = [
{
label: "First Name",
placeHolder: " ",
name: "firstName",
type: "text",
isRequired: true,
section: "",
maxLength: 35,
helperText: `Maximum length should be 35 characters. You have entered ${
watch()?.firstName?.length || 0
} characters.`,
},
{
label: "Last Name",
name: "lastName",
placeHolder: " ",
type: "text",
isRequired: true,
section: "",
maxLength: 35,
helperText: `Maximum length should be 35 characters. You have entered ${
watch()?.lastName?.length || 0
} characters.`,
},
{
label: "Email address",
name: "emailAddress",
placeHolder: " ",
type: "email",
isRequired: true,
section: "",
},
];
// ==================== [Create Form Object] =======================
const formEditFields = [
{
label: "First Name",
placeHolder: " ",
name: "firstName",
type: "text",
isRequired: true,
section: "",
maxLength: 35,
helperText: `Maximum length should be 35 characters. You have entered ${
watch()?.firstName?.length || 0
} characters.`,
},
{
label: "Last Name",
name: "lastName",
placeHolder: " ",
type: "text",
isRequired: true,
section: "",
maxLength: 35,
helperText: `Maximum length should be 35 characters. You have entered ${
watch()?.lastName?.length || 0
} characters.`,
},
{
label: "Email Address",
name: "emailAddress",
placeHolder: " ",
type: "email",
isRequired: true,
section: "",
},
];
// ====================== [Group Create Fields] =========================
const groupedEditFields = formEditFields.reduce((groups, field) => {
const { section } = field;
if (!groups[section]) {
groups[section] = [];
}
groups[section].push(field);
return groups;
}, {});
// ====================== [Group Update Fields] =======================
const groupedFields = formFields.reduce((groups, field) => {
const { section } = field;
if (!groups[section]) {
groups[section] = [];
}
groups[section].push(field);
return groups;
}, {});
// ==================== [On Submit] ========================
// console.log(errors);
const onSubmit = async (data) => {
console.log("Hit");
if (Object.keys(errors).length === 0) {
setForm(data);
setAlert(true);
}
};
return isLoading ? (
<FullscreenLoaders />
) : (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={14}>
{/* ===================== [Switch Button] ======================== */}
<Box
display={"flex"}
justifyContent={"space-between"}
alignItems={"center"}
mt={5}
px={4}
>
<Text
fontSize={"sm"}
mb={0}
onClick={() => navigate(-1)}
cursor={"pointer"}
>
<ArrowBackIcon fontSize={"xl"} me={2} />
{params?.id ? "Edit Details" : "Add Details"}
</Text>
{/* {params?.id ? (
""
) : (
<RoleSwitchButton
isSwitchOn={isSwitchOn}
setIsSwitchOn={setIsSwitchOn}
/>
)} */}
<RoleSwitchButton
isSwitchOn={isSwitchOn}
setIsSwitchOn={setIsSwitchOn}
/>
</Box>
{/* ====================== [Form Input] ====================== */}
<FormInputMain
groupedFields={params?.id ? groupedEditFields : groupedFields}
control={control}
errors={errors}
onSubmit={handleSubmit(onSubmit)}
submitTitle={params?.id ? "Update" : "Submit"}
></FormInputMain>
{/* ======================= [Modal] =========================== */}
<CustomAlertDialog
isOpen={alert}
onClose={() => setAlert(false)}
alertHandler={handleConfirm}
message={
id
? "Are you sure you want to update this?"
: "Are you sure you want to add this?"
}
isLoading={isLoadingBtn}
/>
{/* <DummyComponent /> */}
</Box>
);
};
export default SubAdminUpdateCreate;

View File

@@ -133,7 +133,7 @@ const ViewHistory = () => {
"Status",
];
const extractedArray = filteredData?.map((item, index) => ({
const extractedArray = data?.data?.rows?.map((item, index) => ({
// id: item?.id,
"Sr.no": (
<Text

View File

@@ -7,6 +7,7 @@ import {
TbReportMoney,
} from "react-icons/tb";
import {
RiAccountBoxLine,
RiBankLine,
RiFileUserLine,
RiMoneyDollarBoxLine,
@@ -34,12 +35,12 @@ import { HiOutlineBanknotes } from "react-icons/hi2";
import { AtSignIcon } from "@chakra-ui/icons";
export const nav = [
// {
// title: "Dashboard",
// type: "single",
// path: "/",
// Icon: TbLayoutDashboard,
// },
{
title: "Dashboard",
type: "single",
path: "/",
Icon: TbLayoutDashboard,
},
{
title: "MASTER MENU",
type: "title",
@@ -104,25 +105,23 @@ export const nav = [
title: "INVESTORS REQUEST",
type: "title",
},
{
title: "Fawateer Deposit",
submenu: [
{
title: "Aprover Request",
path: "/fawateer",
icon: RiMoneyDollarBoxLine,
},
{
title: "View History",
path: "/fawateer-history",
icon: RiExchangeBoxLine,
},
],
type: "accordion",
Icon: HiOutlineBanknotes,
}
,
{
title: "Fawateer Deposit",
submenu: [
{
title: "Approver Request",
path: "/fawateer",
icon: RiMoneyDollarBoxLine,
},
{
title: "View History",
path: "/fawateer-history",
icon: RiExchangeBoxLine,
},
],
type: "accordion",
Icon: HiOutlineBanknotes,
},
{
title: "Bank Deposit",
submenu: [
@@ -191,6 +190,34 @@ export const nav = [
type: "accordion",
Icon: AiOutlineUserDelete,
},
{
title: "REVERSAL TRANSACTION",
type: "title",
},
{
title: "Bank Deposit Request",
type: "single",
path: "/bank-deposit-request",
Icon: RiBankLine,
},
{
title: "Fawateer Deposit",
type: "single",
path: "/reversal-fawateer-deposit",
Icon: HiOutlineBanknotes,
},
{
title: "Account Deletion Request",
type: "single",
path: "/account-deletion-request",
Icon: RiAccountBoxLine,
},
{
title: "MANAGE ADMIN",
type: "title",
@@ -233,6 +260,11 @@ export const nav = [
path: "/bank-details",
icon: RiBankLine,
},
{
title: "Sub Admin",
path: "/subadmin",
icon: RiFileUserLine,
},
],
type: "accordion",
Icon: MdOutlineAdminPanelSettings,

View File

@@ -31,7 +31,7 @@ import DepositRequest from "../Pages/Deposit/DepositRequest/DepositRequest";
import EditBankDetails from "../Pages/Admin/BankDetails/EditBankDetails";
import ExchangeHistory from "../Pages/Master/ExchangeRate/ExchangeHistroy";
import Welcome from "../Pages/PaymentSuccess";
import Dashbaord from "../Pages/Dashbaord";
import Dashbaord from "../Pages/Dashboard/Dashbaord";
import UnderConstruction from "../Pages/UnderConstruction";
import PendingRequest from "../Pages/WithDrawal/DrawalRequest/PendingRequest";
import BankInvestor from "../Pages/Admin/Investor/BankInvestor/BankInvestor";
@@ -46,12 +46,19 @@ import EmailNotification from "../Pages/EmailNotification/EmailNotification";
import User from "../Pages/User/User";
import AddUser from "../Pages/User/AddUser";
import Profile from "../Pages/Profile/Profile";
import SubAdmin from "../Pages/SubAdmin/SubAdmin";
import SubAdminUpdateCreate from "../Pages/SubAdmin/SubAdminUpdateCreate";
import InvestmentOpportunities from "../Pages/Dashboard/InvestmentOpportunities";
import BankDepositRequest from "../Pages/BankDepositRequest/BankDepositRequest";
import ReversalFawateerDeposit from "../Pages/ReversalFawateerDeposit/ReversalFawateerDeposit";
import ReversalAccountDeletion from "../Pages/ReversalAccountDeletion/ReversalAccountDeletion";
export const RouteLink = [
// =============[ Tanami ]================
// ===============[ Management]===============
{ path: "/", Component: Sponser },
{ path: "/", Component: Dashbaord },
{ path: "/investment-opportunities", Component: InvestmentOpportunities },
{ path: "/sponser", Component: Sponser },
{ path: "/sponser/add-sponser/:id", Component: AddSponser },
{ path: "/sponser/add-sponser", Component: AddSponser },
@@ -123,7 +130,15 @@ export const RouteLink = [
// { path: "/bank-details", Component: UnderConstruction },
{ path: "/bank-details/edit-bank-details/:id", Component: EditBankDetails },
{ path: "/profile", Component: Profile },
{ path: "/subadmin", Component: SubAdmin },
{ path: "/subadmin/subadmin-update/:id", Component: SubAdminUpdateCreate },
{ path: "/subadmin/subadmin-update", Component: SubAdminUpdateCreate },
// ===============[ REVERSAL TRANSACTION ]===============
{ path: "/bank-deposit-request", Component: BankDepositRequest },
{ path: "/reversal-fawateer-deposit", Component: ReversalFawateerDeposit },
{ path: "/account-deletion-request", Component: ReversalAccountDeletion },
// ===============[ fawateer ]===============
@@ -134,8 +149,5 @@ export const RouteLink = [
// { path: "/fawateer-approver", Component: ApproveRequest },
// { path: "/approver-history", Component: ApproveHistory },
];

View File

@@ -20,9 +20,9 @@ export const banInvestorDetails = createApi({
getUnbanInvestor: builder.query({
query: ({ page, size, searchTerm, userStatus, KYCStatus, country_xid }) => {
query: ({ page, size, search, userStatus, KYCStatus, country_xid }) => {
// Start with the base URL, including searchTerm
let baseURL = `/investorDetails/admin/getAllUnbanned?search=${searchTerm || ""}&userStatus=${userStatus ||""}&KYCStatus=${KYCStatus || ""}&country_xid=${country_xid||""}`;
let baseURL = `/investorDetails/admin/getAllUnbanned?search=${search || ""}&userStatus=${userStatus ||""}&KYCStatus=${KYCStatus || ""}&country_xid=${country_xid||""}`;
// Conditionally append kycStatus if it's defined
if (KYCStatus) {

View File

@@ -0,0 +1,61 @@
// Need to use the React-specific entry point to import createApi
import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./token.serivce";
// Define a service using a base URL and expected endpoints
export const bankDepositRequestMaster = createApi({
reducerPath: "BankDeposit",
baseQuery: baseQuery,
tagTypes: ["getBankDeposit"],
endpoints: (builder) => ({
// ======[Get All]=====
getBankDepositMaster: builder.query({
query: ({ size, search, page }) => {
let baseURL = `/reversal-transactions/bank-transfer/getAll?search=${search || ""}`
// Conditionally append page and size parameters if they are defined
if (page !== undefined && size !== undefined) {
baseURL += `&page=${page}&size=${size}`;
}
return baseURL;
},
providesTags: ["getBankDeposit"],
}),
approveBankDepositRequest: builder.mutation({
query: ({ id, data }) => ({
url: `/reversal-transactions/bank-transfer/approve/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getDepositRequest", "getDepositHistory"],
}),
createBankDepositReversalRequest: builder.mutation({
query: ({ id, data }) => ({
url: `/reversal-transactions/bank-transfer/create/${id}`,
method: "POST",
body: data,
}),
invalidatesTags: ["getDepositRequest", "getDepositHistory"],
}),
rejectbankDepositRequest: builder.mutation({
query: ({ id, data }) => ({
url: `/reversal-transactions/bank-transfer/reject/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getDepositRequest", "getDepositHistory"],
}),
}),
});
export const {
useGetBankDepositMasterQuery,
useApproveBankDepositRequestMutation,
useRejectbankDepositRequestMutation,
useCreateBankDepositReversalRequestMutation,
} = bankDepositRequestMaster;

View File

@@ -0,0 +1,30 @@
// Need to use the React-specific entry point to import createApi
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./token.serivce";
// Define a service using a base URL and expected endpoints
export const changePasswordMake = createApi({
reducerPath: "changePassword",
baseQuery: baseQuery,
tagTypes: ["getPassword"],
endpoints: (builder) => ({
// // ========[ update ]========
updatePassword: builder.mutation({
query: (data) => ({
url: `/auth/admin/update-password`,
method: "POST",
body: data,
}),
invalidatesTags: ["getPassword"],
}),
}),
});
// Export hooks for usage in functional components
export const {
useUpdatePasswordMutation
} = changePasswordMake;

View File

@@ -0,0 +1,20 @@
// Need to use the React-specific entry point to import createApi
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./token.serivce";
// Define a service using a base URL and expected endpoints
export const dashboardMaster = createApi({
reducerPath: "Dashboard",
baseQuery: baseQuery,
tagTypes: ["getDashboard"],
endpoints: (builder) => ({
// ======[Get All]=====
getDashboardMaster: builder.query({
query: () => `/dashboard/admin/`,
providesTags: ["getDashboard"],
}),
}),
});
export const { useGetDashboardMasterQuery } = dashboardMaster;

View File

@@ -88,7 +88,7 @@ export const depositRequest = createApi({
// Export hooks for usage in functional components
export const {
useGetDepositRequestQuery,
useGetDepositRequestByIdQuery,
useGetDepositRequestByIdQuery,
useUpdateDepositRequestMutation,
useDepositRejectMutation,
useGetDepositHistoryQuery,

View File

@@ -9,7 +9,7 @@ import { baseQuery } from "./token.serivce";
export const fawateerRequest = createApi({
reducerPath: "fawateerRequest",
baseQuery: baseQuery,
tagTypes: ["getFawateerRequest" ,"getApproveHistory","getApproveComment","getRejectComment","getFawateerMakerRequest"],
tagTypes: ["getFawateerRequest", "getApproveHistory", "getApproveComment", "getRejectComment", "getFawateerMakerRequest"],
endpoints: (builder) => ({
@@ -37,7 +37,7 @@ export const fawateerRequest = createApi({
}),
getFawateerForMakerRequest: builder.query({
query: ({ page, size, searchTerm }) => {
@@ -50,8 +50,19 @@ export const fawateerRequest = createApi({
providesTags: ["getFawateerMakerRequest"],
}),
// getApproveHistory: builder.query({
// query: () => `/fawateer/admin/getAll`,
// providesTags: ["getApproveHistory"],
// }),
getApproveHistory: builder.query({
query: () => `/fawateer/admin/getAll`,
query: ({ page, size, searchTerm }) => {
let baseURL = `/fawateer/admin/getAll?search=${searchTerm || ""}`;
if (page !== undefined && size !== undefined) {
baseURL += `&page=${page}&size=${size}`; // Only add pagination if both are defined
}
return baseURL;
},
providesTags: ["getApproveHistory"],
}),
@@ -72,16 +83,16 @@ export const fawateerRequest = createApi({
}),
invalidatesTags: ["getFawateerRequest"],
}),
}),
});
// Export hooks for usage in functional components
export const {
useGetFawateerRequestQuery,
useGetApproveHistoryQuery,
useApproveCommentMutation,
useRejectCommentMutation,
useGetFawateerForMakerRequestQuery,
useGetFawateerInvestorsQuery
useGetFawateerRequestQuery,
useGetApproveHistoryQuery,
useApproveCommentMutation,
useRejectCommentMutation,
useGetFawateerForMakerRequestQuery,
useGetFawateerInvestorsQuery
} = fawateerRequest;

View File

@@ -0,0 +1,30 @@
// Need to use the React-specific entry point to import createApi
import { createApi} from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./token.serivce";
// Define a service using a base URL and expected endpoints
export const forgetPasswordMake = createApi({
reducerPath: "forgetPassword",
baseQuery: baseQuery,
tagTypes: ["getPassword"],
endpoints: (builder) => ({
// // ========[ update ]========
forgetPassword: builder.mutation({
query: (data) => ({
url: `/auth/admin/forget-password`,
method: "POST",
body: data,
}),
invalidatesTags: ["getPassword"],
}),
}),
});
// Export hooks for usage in functional components
export const {
useForgetPasswordMutation
} = forgetPasswordMake;

View File

@@ -256,8 +256,9 @@ export const ioService = createApi({
updateCancleStatusTo: builder.mutation({
query: ({ id, data }) => ({
url: `/io/admin/transaction/${id}/cancel`,
url: `/io/admin/maker-transaction/${id}/io-cancel`,
method: "POST",
body:data
}),
invalidatesTags: ["getIOById"],
}),
@@ -421,7 +422,7 @@ export const ioService = createApi({
}),
profile: builder.query({
query: (id) => `/auth/admin/profile`,
query: () => `/auth/admin/profile`,
}),
// ========Add Io Details========
@@ -435,7 +436,7 @@ export const ioService = createApi({
invalidatesTags: ["getIOById"],
}),
updateTransaction: builder.mutation({
query: (id) => ({
// url: `/io/admin/maker-transaction/${id}/verify-pending-transaction-for-cash-and-nav`,
@@ -448,45 +449,45 @@ export const ioService = createApi({
addNavDetails: builder.mutation({
query: ({id,data}) => ({
query: ({ id, data }) => ({
url: `/io/admin/maker-transaction/${id}/io-nav`,
method: "POST",
body:data,
body: data,
}),
invalidatesTags: ["getIOById"],
}),
addIOTransaction: builder.mutation({
query: ({id,data}) => ({
query: ({ id, data }) => ({
url: `/io/admin/maker-transaction/${id}/io-nav`,
method: "POST",
body:data,
body: data,
}),
invalidatesTags: ["getIOById"],
}),
saveIOTransaction: builder.mutation({
query: ({id,data}) => ({
query: ({ id, data }) => ({
url: `/io/admin/maker-transaction/${id}/io-yeild`,
method: "POST",
body:data,
body: data,
}),
invalidatesTags: ["getIOById"],
}),
exitIOTransaction: builder.mutation({
query: ({id,data}) => ({
query: ({ id, data }) => ({
url: `/io/admin/maker-transaction/${id}/io-exit`,
method: "POST",
body:data,
body: data,
}),
invalidatesTags: ["getIOById"],
}),
addIoCase: builder.mutation({
query: (id) => ({
@@ -499,10 +500,10 @@ export const ioService = createApi({
approveIOCase: builder.mutation({
query: ({id,data}) => ({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/io-cash/${id}`,
method: "PATCH",
body:data,
body: data,
}),
invalidatesTags: ["getIOById"],
@@ -510,71 +511,71 @@ export const ioService = createApi({
approveIONav: builder.mutation({
query: ({id,data}) => ({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/io-nav/${id}`,
method: "PATCH",
body:data,
body: data,
}),
invalidatesTags: ["getIOById"],
}),
approveDistribution: builder.mutation({
query: ({id,data}) => ({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/distributed-to-investor/${id}`,
method: "PATCH",
body:data,
body: data,
}),
invalidatesTags: ["getIOById"],
}),
approveExit: builder.mutation({
query: ({id,data}) => ({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/exit/${id}`,
method: "PATCH",
body:data,
body: data,
}),
invalidatesTags: ["getIOById"],
}),
approveInvested: builder.mutation({
query: ({id,data}) => ({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/amount-invested/${id}`,
method: "PATCH",
body:data,
body: data,
}),
invalidatesTags: ["getIOById"],
}),
approveDistributed: builder.mutation({
query: ({id,data}) => ({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/distributed-to-investor/${id}`,
method: "PATCH",
body:data,
body: data,
}),
invalidatesTags: ["getIOById"],
}),
approveExitTransaction: builder.mutation({
query: ({id,data}) => ({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/exit/${id}`,
method: "PATCH",
body:data,
body: data,
}),
invalidatesTags: ["getIOById"],
}),
approveCancleTransaction: builder.mutation({
query: ({id,data}) => ({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/cancel/${id}`,
method: "PATCH",
body:data,
body: data,
}),
invalidatesTags: ["getIOById"],
@@ -582,16 +583,16 @@ export const ioService = createApi({
rejectIOCase: builder.mutation({
query: ({id,data}) => ({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/reject/${id}`,
method: "PATCH",
body:data,
body: data,
}),
invalidatesTags: ["getIOById"],
}),
}),
});

View File

@@ -0,0 +1,61 @@
// Need to use the React-specific entry point to import createApi
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./token.serivce";
// Define a service using a base URL and expected endpoints
export const reversalAccountDeletionMaster = createApi({
reducerPath: "accountDeletion",
baseQuery: baseQuery,
tagTypes: ["getAccountDeletion", "getHistory"],
endpoints: (builder) => ({
// ======[Get All]=====
getAccountDeletionMaster: builder.query({
query: ({ page, size, search, }) => {
let baseURL = `/reversal-transactions/account-deletion/getAll?search=${search || ""}`
// Conditionally append page and size parameters if they are defined
if (page !== undefined && size !== undefined) {
baseURL += `&page=${page}&size=${size}`;
}
return baseURL;
},
providesTags: ["getAccountDeletion"],
}),
approveAccountDeletionRequest: builder.mutation({
query: ({ id, data }) => ({
url: `/reversal-transactions/account-deletion/approve/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getAccountDeletion", "getHistory"],
}),
createAccountDeletionReversalRequest: builder.mutation({
query: ({ id, data }) => ({
url: `/reversal-transactions/account-deletion/create/${id}`,
method: "POST",
body: data,
}),
invalidatesTags: ["getAccountDeletion", "getHistory"],
}),
rejectAccountDeletionRequest: builder.mutation({
query: ({ id, data }) => ({
url: `/reversal-transactions/account-deletion/reject/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getAccountDeletion", "getHistory"],
}),
}),
});
export const {
useGetAccountDeletionMasterQuery,
useCreateAccountDeletionReversalRequestMutation,
useApproveAccountDeletionRequestMutation,
useRejectAccountDeletionRequestMutation
} = reversalAccountDeletionMaster;

View File

@@ -0,0 +1,60 @@
// Need to use the React-specific entry point to import createApi
import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./token.serivce";
// Define a service using a base URL and expected endpoints
export const reversalFawateerDepositMaster = createApi({
reducerPath: "FawateerDeposit",
baseQuery: baseQuery,
tagTypes: ["getFawateerDeposit", "getDepositHistory"],
endpoints: (builder) => ({
// ======[Get All]=====
getFawateerDepositMaster: builder.query({
query: ({ search, page, size }) => {
let baseURL = `/reversal-transactions/fawateer/getAll?search=${search || ""}`
// Conditionally append page and size parameters if they are defined
if (page !== undefined && size !== undefined) {
baseURL += `&page=${page}&size=${size}`;
}
return baseURL;
},
providesTags: ["getFawateerDeposit"],
}),
approveFawateerRequest: builder.mutation({
query: ({ id, data }) => ({
url: `/reversal-transactions/fawateer/approve/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getFawateerDeposit", "getDepositHistory"],
}),
createFawateerReversalRequest: builder.mutation({
query: ({ id, data }) => ({
url: `/reversal-transactions/fawateer/create/${id}`,
method: "POST",
body: data,
}),
invalidatesTags: ["getFawateerDeposit", "getDepositHistory"],
}),
rejectFawateerRequest: builder.mutation({
query: ({ id, data }) => ({
url: `/reversal-transactions/fawateer/reject/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getFawateerDeposit", "getDepositHistory"],
}),
}),
});
export const {
useGetFawateerDepositMasterQuery,
useApproveFawateerRequestMutation,
useCreateFawateerReversalRequestMutation,
useRejectFawateerRequestMutation,
} = reversalFawateerDepositMaster;

View File

@@ -0,0 +1,84 @@
// Need to use the React-specific entry point to import createApi
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./token.serivce";
// Define a service using a base URL and expected endpoints
export const sabAdminMaster = createApi({
reducerPath: "sabAdminMaster",
baseQuery: baseQuery,
tagTypes: ["getSubAdmin", "prePopulate","getSubAdminById"],
endpoints: (builder) => ({
// ======[Get All]=====
getSubAdminMaster: builder.query({
query: () => `/subadmin/admin/getAll`,
providesTags: ["getSubAdmin"],
}),
// // ========[ Create ]========
createSubAdmin: builder.mutation({
query: (data) => ({
url: `/subadmin/admin/create`,
method: "POST",
body: data,
}),
invalidatesTags: ["getSubAdmin", "prePopulate"],
}),
// // ========[Update Sponser]========
updateSubAdmin: builder.mutation({
query: ({ data, id }) => ({
url: `/subadmin/admin/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getSubAdmin","getSubAdminById"],
}),
getSubAdminById: builder.query({
query: (id) => `/subadmin/admin/${id}`,
providesTags: ["getSubAdminById"],
}),
// // ========[Toggle Status]========
toggleStatus: builder.mutation({
query: ({ id, data }) => ({
url: `/subadmin/admin/toggle-role/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getSubAdmin"],
}),
// ==========[Delete User] ==========
deleteUser: builder.mutation({
query: (id) => ({
url: `/subadmin/admin/${id}`,
method: "DELETE",
}),
invalidatesTags: ["getSubAdmin"],
}),
}),
});
// Export hooks for usage in functional components
export const {
useGetSubAdminMasterQuery,
useCreateSubAdminMutation,
useUpdateSubAdminMutation,
useGetSubAdminByIdQuery,
useToggleStatusMutation,
useDeleteUserMutation,
} = sabAdminMaster;

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