Compare commits
393 Commits
sprint4.1
...
537e1520bf
| Author | SHA1 | Date | |
|---|---|---|---|
| 537e1520bf | |||
|
|
b141743190 | ||
|
|
f9d0b3ce6c | ||
|
|
965263a3f2 | ||
|
|
04a5d1280f | ||
|
|
d40eadb35e | ||
|
|
f4a2fd2889 | ||
|
|
96f813f632 | ||
|
|
8587b91a33 | ||
|
|
49b06e81a6 | ||
|
|
79ec8d92ae | ||
|
|
f2d8aee6a9 | ||
|
|
a108dcdb17 | ||
|
|
41a1bde62b | ||
|
|
efddfe6d7a | ||
|
|
abe9b14436 | ||
|
|
f31b67f676 | ||
|
|
066e2fc169 | ||
|
|
2892334e41 | ||
|
|
103959b40b | ||
|
|
714804fdd6 | ||
|
|
af2485ba58 | ||
|
|
2b89e52e58 | ||
|
|
05f1d4055e | ||
|
|
efbb72ca4c | ||
|
|
fba6a7ad45 | ||
|
|
0ccfba1238 | ||
|
|
5e86a72700 | ||
|
|
9f93993938 | ||
|
|
79c822bb4d | ||
|
|
7523fd48f2 | ||
|
|
7e8f5e1115 | ||
|
|
ccd27b7840 | ||
|
|
27b7e31930 | ||
|
|
27d243ee30 | ||
|
|
ba06bf28c0 | ||
|
|
cbfeff4710 | ||
|
|
c82ef27958 | ||
|
|
067edfb4a5 | ||
|
|
92977c4bed | ||
|
|
c7856baf45 | ||
|
|
27d3167acc | ||
|
|
9b5dd51660 | ||
|
|
7f3dd5aa79 | ||
|
|
584df9b140 | ||
|
|
31d237b675 | ||
|
|
3f5e154e23 | ||
|
|
bb9513fcfe | ||
|
|
d196dffd12 | ||
|
|
f9b1f820c2 | ||
|
|
6d2964036a | ||
|
|
21eb9e41ff | ||
|
|
87f8977c73 | ||
|
|
390ec27e35 | ||
|
|
5ff437970e | ||
|
|
1beeeed8d3 | ||
|
|
401c9daf75 | ||
|
|
d23bf5d7e9 | ||
|
|
20213408c4 | ||
|
|
3d456c3c48 | ||
|
|
4e4de8caf5 | ||
|
|
150194cc26 | ||
|
|
5928af4283 | ||
|
|
55a397594e | ||
|
|
874940ae7c | ||
|
|
96a302d262 | ||
| 35d3e07224 | |||
|
|
651c775c2a | ||
|
|
18035047d4 | ||
|
|
a51585089c | ||
|
|
ebcb06bf5e | ||
|
|
84dc47b447 | ||
|
|
aeb0f25663 | ||
|
|
a07d011c85 | ||
|
|
0ed01bf94f | ||
|
|
96f8c32483 | ||
|
|
45f69fe2b7 | ||
|
|
57c6923784 | ||
|
|
974d1501b2 | ||
|
|
c33e358e8e | ||
|
|
1434088c1b | ||
|
|
3c6f083432 | ||
|
|
f81b210b0a | ||
|
|
5743cadf5e | ||
|
|
4579573f23 | ||
|
|
625f721325 | ||
|
|
01aece9bf6 | ||
|
|
d9692c3890 | ||
|
|
6e42bc0adb | ||
|
|
c9e5223989 | ||
|
|
69f76bbdce | ||
|
|
25df0d6160 | ||
|
|
51727d4de1 | ||
|
|
1539493641 | ||
|
|
d63ac2eb2b | ||
|
|
9eca3ae9fc | ||
| 6c2a38becb | |||
|
|
212f5d4d37 | ||
|
|
b620cd410d | ||
|
|
84298ff453 | ||
| bddf7381a6 | |||
|
|
8eae4222f4 | ||
| 52a987b616 | |||
|
|
2a3c211b56 | ||
| 4d6a8bb472 | |||
|
|
41a60c0892 | ||
|
|
5c05a68bb0 | ||
| f02f9c8b7d | |||
|
|
137912aa11 | ||
| e3fe5a1618 | |||
|
|
edcb4cd7b9 | ||
| c72f3ece4e | |||
|
|
85be7c891e | ||
|
|
e41d6b17b9 | ||
| 3217605a0f | |||
| b4d28387fa | |||
|
|
5411d4cd18 | ||
|
|
6a0aa17e2d | ||
|
|
471e4f32ab | ||
|
|
5d2c28f6ca | ||
|
|
49f39e1c2c | ||
|
|
f9c7bf9d5d | ||
|
|
49beb9539a | ||
|
|
ed27ed6939 | ||
|
|
d84b3a0e35 | ||
|
|
20c0c7840f | ||
|
|
bbfd617b27 | ||
|
|
87e0716383 | ||
|
|
c7d6a0fe36 | ||
|
|
c83aaa411a | ||
|
|
a2fe1435cb | ||
| 6a9e0f9908 | |||
|
|
9c0b231e62 | ||
|
|
4ca28fd910 | ||
|
|
fed5125660 | ||
|
|
8a297d02ef | ||
|
|
2c8965c16a | ||
| 9d4d5301e5 | |||
| 88dc9d14fe | |||
|
|
d567acfec8 | ||
|
|
463325e603 | ||
| 84f869d9bd | |||
| 606ac68d85 | |||
|
|
537304f0fb | ||
|
|
3b83b625c3 | ||
|
|
694dde0f14 | ||
|
|
4f8916036e | ||
|
|
0c21e99732 | ||
|
|
5900f637be | ||
|
|
2453b24d91 | ||
|
|
3db9488444 | ||
| bb30a71a60 | |||
|
|
526d2aecca | ||
| e742f6c18d | |||
|
|
d4c9a5521f | ||
| 7a04ee0abb | |||
|
|
a12b2c9a2c | ||
| 0b2e48200a | |||
|
|
bf7b3e1596 | ||
| 059b711bc1 | |||
| b0c01f3a0c | |||
| 272e94caf0 | |||
| cd95aa35a7 | |||
| 973ec656d0 | |||
|
|
29a49150b7 | ||
| 7a908a3284 | |||
|
|
23c3997d06 | ||
| 6779fdd672 | |||
|
|
0ab898a3da | ||
|
|
f3a8a80f4d | ||
| d1cbeee294 | |||
|
|
1c4f975781 | ||
| 17ffb864a3 | |||
|
|
6ffcff58a2 | ||
| 82612fa7f2 | |||
|
|
470ba49c00 | ||
| b29478a939 | |||
|
|
7ba524d2e4 | ||
| cc58cdc9b7 | |||
|
|
9f54bfbc65 | ||
| 992cb13e1e | |||
|
|
85e3c34120 | ||
| 46672a34e2 | |||
|
|
61e49393fe | ||
|
|
5b2efcd292 | ||
|
|
5fc16b58ea | ||
| 95c629533e | |||
|
|
06548abf1e | ||
|
|
f2023cf7b3 | ||
|
|
77fc645767 | ||
|
|
9740fff33c | ||
| 0f678fefc1 | |||
|
|
986e531896 | ||
|
|
734dff43a1 | ||
| d244df302b | |||
|
|
eb14139bcf | ||
|
|
f5e7217304 | ||
|
|
30c51a5b34 | ||
| 1f89ca0a1c | |||
|
|
82215f8569 | ||
|
|
0199a46ed5 | ||
|
|
8cfccf656d | ||
|
|
2e06b52881 | ||
| f99e78a8df | |||
|
|
6f5e83a4bc | ||
|
|
fff3689aeb | ||
|
|
184f42ef59 | ||
| ccc75f50e5 | |||
| 751aa6c673 | |||
| d44b8aea0d | |||
| 5fd9fda73b | |||
| 6216367d21 | |||
| a1b9f1c507 | |||
| 5ff6d5d07b | |||
| 7f1106449f | |||
| da55f00f73 | |||
| 0c56759251 | |||
| cd1cf86fc9 | |||
| 9cd0a4e9c6 | |||
| 0b080733fb | |||
|
|
94b8be1130 | ||
|
|
3079e7f269 | ||
|
|
2147914c81 | ||
|
|
3696ec5e59 | ||
|
|
7f5d3065ca | ||
| 28990bb9d6 | |||
| 2f816f3c45 | |||
|
|
61c81b100c | ||
|
|
fd22732648 | ||
| 2df6ea41a4 | |||
|
|
739b755ec1 | ||
| 6c16d142d6 | |||
|
|
4a54d5c80e | ||
|
|
47d75371bc | ||
|
|
bc8f78d8d6 | ||
| 915242c1f3 | |||
|
|
30a9226f86 | ||
| a6dd60140a | |||
| 512e2936ad | |||
| c9edca64c8 | |||
| 054323978c | |||
|
|
a12ab874cf | ||
|
|
14b24ffa00 | ||
| e47db6c840 | |||
|
|
48e53ea176 | ||
| 36cf9fa610 | |||
| 8a6d9102cb | |||
|
|
88bea104f6 | ||
| 417164cb16 | |||
|
|
c0ce32219e | ||
|
|
aff088c7b7 | ||
| a7d3703244 | |||
| c41deb0534 | |||
| 92ebf64223 | |||
| 36a68e2169 | |||
| 46aa0c4631 | |||
|
|
bd48e3fb06 | ||
|
|
90a433e312 | ||
| 8cd6a65143 | |||
| 105f103fda | |||
| aa6c61e4a4 | |||
| b08030a412 | |||
|
|
149667436e | ||
|
|
bb724d88d1 | ||
|
|
1fabbd9b77 | ||
| c51ae9081f | |||
| cfd811708a | |||
| 6630723092 | |||
| 2ae81ef032 | |||
| 10221c03d9 | |||
| 084641c561 | |||
| 7b24db3e00 | |||
| f8b33e3eb5 | |||
| 9fa42845c7 | |||
|
|
4bded669bf | ||
| bc8d986f29 | |||
| f5497ea6ac | |||
|
|
7f1ef99b6c | ||
| 28978622f3 | |||
|
|
9dca99b1c1 | ||
|
|
8546ca3247 | ||
|
|
ae1cdc8811 | ||
|
|
1a1910c58c | ||
|
|
fdb9ccefa9 | ||
|
|
a126d6515d | ||
|
|
ab175b4c76 | ||
|
|
c9c7a7be69 | ||
|
|
da70893c2b | ||
|
|
a53069f6f6 | ||
|
|
8b43ff77a9 | ||
|
|
8cb693a4c4 | ||
|
|
7b91ad2720 | ||
|
|
416c5ce8aa | ||
| a0a1849a79 | |||
|
|
a1d7b1e9f9 | ||
| 3a327d63e0 | |||
| 753f3d40c3 | |||
|
|
226b8a39bc | ||
| fdbdc61cac | |||
| d1cc24a43b | |||
| c766b29c4b | |||
| 4ee94c1261 | |||
|
|
c2d75c340c | ||
| 8cb25e9189 | |||
| d74481e7f4 | |||
|
|
2281550345 | ||
|
|
6ca97a7ed3 | ||
| 4b3313b1ea | |||
|
|
7a8ea42832 | ||
| 1be4a00ced | |||
|
|
c5d9b32e8d | ||
|
|
4957bd5032 | ||
|
|
f151b67e49 | ||
| b92de5e990 | |||
| 33bbd5f3e6 | |||
|
|
0ab8ac73aa | ||
| 410f18f512 | |||
| 29fdb99366 | |||
|
|
867ad9e7b9 | ||
| 92fe9f66f9 | |||
| d2d18d435f | |||
| ede34c1b41 | |||
| 639a6cfae9 | |||
| 68026ddd66 | |||
|
|
be451086b9 | ||
|
|
566d170226 | ||
| 60d3c7572d | |||
|
|
dceec17c58 | ||
|
|
a041cea256 | ||
|
|
91e362d6c2 | ||
| b3c0662e77 | |||
|
|
86ce31e86c | ||
|
|
0df81c1169 | ||
| 31eb1605bf | |||
| 8dc5eecad9 | |||
|
|
508d7fe5b5 | ||
|
|
9208535613 | ||
|
|
9916d6618e | ||
| 2f7f907a89 | |||
| 8b0454b16a | |||
|
|
f7b06bfd19 | ||
|
|
64b881dd89 | ||
|
|
fba3ad2212 | ||
|
|
026a563d84 | ||
| b713845df6 | |||
| 79bcf851d9 | |||
| 3a7d174766 | |||
|
|
6d315ba2ef | ||
|
|
7748872e29 | ||
|
|
b5f7250691 | ||
| 0db739c557 | |||
| 3165c11af7 | |||
| c97de1314e | |||
| 287fb80f9e | |||
| b690a55ed3 | |||
|
|
b8390bfad9 | ||
|
|
5e4ebe3fb5 | ||
|
|
8b97add9e0 | ||
| 25226d5464 | |||
| 3131ff8610 | |||
|
|
187841b178 | ||
|
|
849b89efc8 | ||
| 116db3b922 | |||
|
|
4313057012 | ||
|
|
cdd0d670ba | ||
| 7c3a6112d2 | |||
| e3ccc3efbc | |||
|
|
129a79c669 | ||
| b7ce3de128 | |||
|
|
97cfd45545 | ||
|
|
e0664d8e21 | ||
| d009848068 | |||
|
|
afb538cc22 | ||
|
|
e0b48abfd5 | ||
| 9083d94813 | |||
| 323d09f88d | |||
| 46d8031d4d | |||
| fb39e32b95 | |||
| 670e4d36a9 | |||
|
|
26777e2ada | ||
| 51f3b512a7 | |||
|
|
d7012f2692 | ||
|
|
e07a92ba03 | ||
|
|
adb4bd5d27 | ||
| cfda3264fc | |||
|
|
1569afe4f0 | ||
|
|
4a3072b4d3 | ||
| 4516c70406 | |||
| 50f87869be | |||
| d69e4a203f | |||
| 6e4c794d2b | |||
| 620b365437 | |||
| 2e5ecb967f |
28
.env.example
Normal 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
@@ -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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/src/assets/favicon.png" />
|
||||
<link rel="icon" type="image/svg+xml" href="/src/assets/favicons.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Tanami Admin</title>
|
||||
</head>
|
||||
|
||||
2357
package-lock.json
generated
@@ -23,6 +23,7 @@
|
||||
"dotenv": "^16.4.5",
|
||||
"framer-motion": "^11.1.5",
|
||||
"js-cookie": "^3.0.5",
|
||||
"quill": "^2.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-apexcharts": "^1.4.1",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
@@ -30,7 +31,8 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.51.3",
|
||||
"react-icons": "^5.1.0",
|
||||
"react-quill": "^0.0.2",
|
||||
"react-phone-input-2": "^2.15.1",
|
||||
"react-quill": "^2.0.0",
|
||||
"react-redux": "^9.1.1",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"redux-persist": "^6.0.0",
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
/* font-family: "Lato", sans-serif !important; */
|
||||
}
|
||||
|
||||
::selection {
|
||||
background-color: #004717; /* Change this to your desired color */
|
||||
color: white; /* Optional: Change the text color */
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
12
src/App.jsx
@@ -6,6 +6,7 @@ import {
|
||||
Routes,
|
||||
Route,
|
||||
Navigate,
|
||||
useNavigate,
|
||||
} from "react-router-dom";
|
||||
import "./App.css"; // Import CSS file
|
||||
import DefaultLayout from "./Layout/DefaultLayout";
|
||||
@@ -14,6 +15,8 @@ import Login from "./Pages/Login";
|
||||
import GlobalStateContext from "./Contexts/GlobalStateContext";
|
||||
import Cookies from "js-cookie";
|
||||
import NoInternetScreen from "./Pages/NoInternetScreen";
|
||||
import Welcome from "./Pages/PaymentSuccess";
|
||||
import PaymentFailed from "./Pages/PaymentFailed";
|
||||
|
||||
const App = () => {
|
||||
// const { isAuthenticate } = useSelector((state) => state?.auth);
|
||||
@@ -24,6 +27,8 @@ const App = () => {
|
||||
const [isOnline, setIsOnline] = useState(navigator.onLine);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
|
||||
const handleOnlineStatusChange = () => {
|
||||
setIsOnline(navigator.onLine);
|
||||
};
|
||||
@@ -52,12 +57,15 @@ const App = () => {
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/payment-success" element={<Welcome />} />
|
||||
<Route path="/payment-failed" element={<PaymentFailed />} />
|
||||
<Route
|
||||
path="/*"
|
||||
element={
|
||||
// isOnline ? (
|
||||
// isAuthenticate || isAuthenticatedInCookie === "true" ? (
|
||||
localStorage.getItem('accessToken') && localStorage.getItem('refreshToken') ? (
|
||||
isAuthenticate || isAuthenticatedInCookie === "true" ? (
|
||||
// localStorage.getItem('accessToken') && localStorage.getItem('refreshToken') ? (
|
||||
// true ? (
|
||||
<DefaultLayout isOnline={isOnline} />
|
||||
) : (
|
||||
<Login />
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,37 +1,32 @@
|
||||
import React, { forwardRef } from 'react';
|
||||
import { Input } from "@chakra-ui/react";
|
||||
|
||||
// export const formatCurrency = (value) => {
|
||||
// if (!value) return '';
|
||||
// const [integer, decimal] = value.split('.');
|
||||
// const formattedInteger = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
// return decimal ? `${formattedInteger}.${decimal}` : formattedInteger;
|
||||
// };
|
||||
|
||||
export const formatCurrency = (value) => {
|
||||
if (!value) return '';
|
||||
const [integer, decimal] = value?.split('.');
|
||||
const formattedInteger = integer?.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
return decimal ? `${formattedInteger}.${decimal}` : formattedInteger;
|
||||
if (value === undefined || value === null) return '';
|
||||
const [integer, decimal] = String(value).split('.');
|
||||
const formattedInteger = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
return decimal !== undefined ? `${formattedInteger}.${decimal}` : formattedInteger;
|
||||
};
|
||||
|
||||
const CurrencyInput = forwardRef(({ value, onChange, ...props }, ref) => {
|
||||
|
||||
|
||||
|
||||
const handleChange = (event) => {
|
||||
let { value } = event?.target;
|
||||
|
||||
// Remove non-numeric characters except decimal point
|
||||
value = value?.replace(/[^0-9.]/g, '');
|
||||
value = value.replace(/[^0-9.]/g, '');
|
||||
|
||||
// Ensure only one decimal point
|
||||
const parts = value?.split('.');
|
||||
const parts = value.split('.');
|
||||
if (parts.length > 2) {
|
||||
value = parts[0] + '.' + parts?.slice(1)?.join('');
|
||||
value = parts[0] + '.' + parts.slice(1).join('');
|
||||
}
|
||||
|
||||
onChange(value); // Pass the raw value to parent or use it directly
|
||||
// Restrict to two decimal places
|
||||
if (parts[1]?.length > 2) {
|
||||
value = parts[0] + '.' + parts[1].slice(0, 2);
|
||||
}
|
||||
|
||||
onChange(value); // Pass the raw value to parent
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -39,6 +34,7 @@ const CurrencyInput = forwardRef(({ value, onChange, ...props }, ref) => {
|
||||
{...props}
|
||||
ref={ref} // Forward ref here
|
||||
type="text"
|
||||
focusBorderColor="forestGreen.300"
|
||||
value={formatCurrency(value)}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
@@ -46,3 +42,50 @@ const CurrencyInput = forwardRef(({ value, onChange, ...props }, ref) => {
|
||||
});
|
||||
|
||||
export default CurrencyInput;
|
||||
|
||||
|
||||
|
||||
|
||||
// import React, { forwardRef } from 'react';
|
||||
// import { Input } from "@chakra-ui/react";
|
||||
|
||||
// export const formatCurrency = (value) => {
|
||||
// if (value === undefined || value === null) return ''; // Handle undefined or null values
|
||||
// const [integer, decimal] = String(value).split('.'); // Convert value to string before splitting
|
||||
// const formattedInteger = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
// return decimal ? `${formattedInteger}.${decimal}` : formattedInteger;
|
||||
// };
|
||||
|
||||
// const CurrencyInput = forwardRef(({ value, onChange, ...props }, ref) => {
|
||||
|
||||
// const handleChange = (event) => {
|
||||
// let { value } = event?.target;
|
||||
|
||||
// // Remove non-numeric characters except decimal point
|
||||
// value = value?.replace(/[^0-9.]/g, '');
|
||||
|
||||
// // Ensure only one decimal point and restrict to two decimal places
|
||||
// const parts = value?.split('.');
|
||||
// if (parts.length > 2) {
|
||||
// value = parts[0] + '.' + parts?.slice(1)?.join('');
|
||||
// }
|
||||
|
||||
// if (parts[1]?.length > 2) {
|
||||
// value = parts[0] + '.' + parts[1]?.slice(0, 2);
|
||||
// }
|
||||
|
||||
// onChange(value); // Pass the raw value to parent or use it directly
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <Input
|
||||
// {...props}
|
||||
// ref={ref} // Forward ref here
|
||||
// type="text"
|
||||
// value={formatCurrency(value)}
|
||||
// onChange={handleChange}
|
||||
// />
|
||||
// );
|
||||
// });
|
||||
|
||||
// export default CurrencyInput;
|
||||
|
||||
@@ -65,7 +65,7 @@ const DataTable = ({
|
||||
{/* <Box mb={2}>{caption}</Box> */}
|
||||
<Table size="sm" {...provided.droppableProps} ref={provided.innerRef}>
|
||||
<TableCaption p={0}>{caption}</TableCaption>
|
||||
<Thead backgroundColor="gray.50">
|
||||
<Thead backgroundColor="forestGreen.100">
|
||||
<Tr>
|
||||
{tableHeadRow.map((heading, index) => (
|
||||
<Th
|
||||
@@ -97,7 +97,7 @@ const DataTable = ({
|
||||
bg: "blue.50",
|
||||
cursor: "grab",
|
||||
}}
|
||||
bg={snapshot.isDragging ? "blue.100" : "white"}
|
||||
bg={snapshot.isDragging ? "blue.100" : index % 2 ? "white" : "forestGreen.50"}
|
||||
boxShadow={snapshot.isDragging ? "0 0 1em rgba(0, 0, 0, 0.2)" : "none"}
|
||||
|
||||
>
|
||||
@@ -121,7 +121,7 @@ const DataTable = ({
|
||||
)}
|
||||
</Draggable>
|
||||
) : (
|
||||
<Tr key={index}>
|
||||
<Tr bg={index % 2 ? "forestGreen.50" : "white"} key={index}>
|
||||
{tableHeadRow.map((heading, i) => (
|
||||
<Td
|
||||
textAlign={tableHeadRow.length - 1 === i || centered ? "center" : "left"}
|
||||
|
||||
@@ -9,79 +9,170 @@ import {
|
||||
Tr,
|
||||
Skeleton,
|
||||
TableCaption,
|
||||
Tfoot,
|
||||
Checkbox,
|
||||
Radio,
|
||||
} from "@chakra-ui/react";
|
||||
import EmptySearchList from "../EmptySearchList";
|
||||
import { TABLE_PAGINATION } from "../../Constants/Paginations";
|
||||
|
||||
const DataTable = ({
|
||||
const NormalTable = ({
|
||||
data,
|
||||
isLoading,
|
||||
tableHeadRow,
|
||||
emptyMessage,
|
||||
centered,
|
||||
}) => {
|
||||
total,
|
||||
showRadioButton, // Prop for showing the checkboxes
|
||||
selectedRadio,
|
||||
setSelectedRadio, // State for managing selected checkboxes
|
||||
|
||||
console.log(data);
|
||||
|
||||
handleCheckboxChange: radioChange,
|
||||
radio
|
||||
}) => {
|
||||
const columnWidth =
|
||||
data && data[0]
|
||||
? `${(100 / Object.keys(data[0]).length).toFixed(2)}%`
|
||||
: "auto";
|
||||
|
||||
// Toggle checkbox selection for individual rows
|
||||
// const handleCheckboxChange = (value) => {
|
||||
// setSelectedRadio((prev) => {
|
||||
// if (prev.includes(value)) {
|
||||
// // Remove if already selected
|
||||
// return prev.filter((id) => id !== value);
|
||||
// } else {
|
||||
// // Add to selected
|
||||
// return [...prev, value];
|
||||
// }
|
||||
// });
|
||||
// };
|
||||
|
||||
// Handle "Check All" checkbox
|
||||
const handleCheckAllChange = () => {
|
||||
if (selectedRadio.length === data.length) {
|
||||
setSelectedRadio([]); // Deselect all if already selected
|
||||
} else {
|
||||
const allIds = data.map((item) => item.id);
|
||||
setSelectedRadio(allIds); // Select all
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const handleCheckboxChange = (value) => {
|
||||
if (radio) {
|
||||
// If radio is true, select only one option
|
||||
setSelectedRadio([value]); // Set the selected radio to this value only
|
||||
} else {
|
||||
// Handle multiple selection for checkboxes
|
||||
setSelectedRadio((prev) => {
|
||||
if (prev.includes(value)) {
|
||||
// Remove if already selected
|
||||
return prev.filter((id) => id !== value);
|
||||
} else {
|
||||
// Add to selected
|
||||
return [...prev, value];
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<TableContainer overflowX={"hidden"} className="h-auto mb-3 w-100">
|
||||
<TableContainer overflowX={"auto"} className="h-auto w-100 table-scroll">
|
||||
{data?.length === 0 ? (
|
||||
<EmptySearchList message={emptyMessage} />
|
||||
) : (
|
||||
<Table size="sm">
|
||||
<TableCaption>Tanami v1.0.0</TableCaption>
|
||||
<Thead backgroundColor="gray.50">
|
||||
<TableCaption p={total ? 0 : null}>
|
||||
{total ? total : "Tanami v1.0.0"}
|
||||
</TableCaption>
|
||||
<Thead bg="forestGreen.100">
|
||||
<Tr>
|
||||
{showRadioButton &&(
|
||||
<Th
|
||||
color={"purple.900"}
|
||||
textAlign={"center"}
|
||||
p={4}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
textTransform={"none"}
|
||||
>
|
||||
{radio? "Select":<Checkbox
|
||||
isChecked={selectedRadio?.length === data?.length}
|
||||
onChange={handleCheckAllChange}
|
||||
colorScheme="forestGreen"
|
||||
|
||||
/>}
|
||||
</Th>
|
||||
)}
|
||||
{tableHeadRow.map((heading, index) => (
|
||||
<Th
|
||||
color={"purple.900"}
|
||||
textAlign={
|
||||
tableHeadRow.length - 1 === index || centered
|
||||
? "center"
|
||||
: "left"
|
||||
}
|
||||
key={index}
|
||||
p={3}
|
||||
width="20px" // Adjust width as needed
|
||||
color={"#004118"}
|
||||
whiteSpace="normal" // Allow text to wrap
|
||||
wordBreak="normal" // Ensure long words break properly
|
||||
overflowWrap="normal" // Break long words if necessary
|
||||
textTransform={'none'}
|
||||
|
||||
p={4}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
textTransform={"none"}
|
||||
>
|
||||
{isLoading ? <Skeleton height="20px" /> : heading}
|
||||
{/* {heading} */}
|
||||
</Th>
|
||||
))}
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody className="web-text-small">
|
||||
{isLoading
|
||||
? Array.from({ length: TABLE_PAGINATION?.size }).map((_, index) => (
|
||||
<Tr key={index}>
|
||||
{tableHeadRow.map((_, i) => (
|
||||
<Td
|
||||
width={'fit-content'}
|
||||
key={i}
|
||||
style={{
|
||||
whiteSpace: "nowrap",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
className="web-text-small"
|
||||
w={columnWidth}
|
||||
>
|
||||
<Skeleton height="20px" mb={1} mt={1} />
|
||||
</Td>
|
||||
))}
|
||||
</Tr>
|
||||
))
|
||||
? Array.from({ length: TABLE_PAGINATION?.size }).map(
|
||||
(_, index) => (
|
||||
<Tr
|
||||
bg={index % 2 === 0 ? "white" : "forestGreen.50"}
|
||||
key={index}
|
||||
>
|
||||
{tableHeadRow.map((_, i) => (
|
||||
<Td
|
||||
key={i}
|
||||
style={{
|
||||
whiteSpace: "nowrap",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
className="web-text-small"
|
||||
>
|
||||
<Skeleton height="20px" mb={1} mt={1} />
|
||||
</Td>
|
||||
))}
|
||||
</Tr>
|
||||
)
|
||||
)
|
||||
: data?.map((item, index) => (
|
||||
<Tr key={index}>
|
||||
<Tr
|
||||
cursor={"pointer"}
|
||||
transition={"0.2s all"}
|
||||
maxH={8}
|
||||
bg={index % 2 === 0 ? "" : "forestGreen.50"}
|
||||
key={index}
|
||||
>
|
||||
{showRadioButton && (
|
||||
<Td textAlign={"center"}>
|
||||
{radio ? <Radio
|
||||
bg={"#fff"}
|
||||
colorScheme="forestGreen"
|
||||
value={item.id}
|
||||
isChecked={selectedRadio.includes(item.id)}
|
||||
onChange={() => radioChange(item.id, item)}
|
||||
/>:<Checkbox
|
||||
bg={"#fff"}
|
||||
colorScheme="forestGreen"
|
||||
value={item.id}
|
||||
isChecked={selectedRadio.includes(item.id)}
|
||||
onChange={() => handleCheckboxChange(item.id)}
|
||||
/>}
|
||||
</Td>
|
||||
)}
|
||||
{tableHeadRow.map((heading, i) => (
|
||||
<Td
|
||||
textAlign={
|
||||
@@ -91,6 +182,7 @@ const DataTable = ({
|
||||
}
|
||||
color={"gray.600"}
|
||||
key={i}
|
||||
fontWeight={500}
|
||||
style={{
|
||||
whiteSpace: "nowrap",
|
||||
textOverflow: "ellipsis",
|
||||
@@ -109,4 +201,4 @@ const DataTable = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default DataTable;
|
||||
export default NormalTable;
|
||||
|
||||
@@ -22,12 +22,14 @@ import {
|
||||
Td,
|
||||
InputGroup,
|
||||
InputRightAddon,
|
||||
HStack,
|
||||
} from "@chakra-ui/react";
|
||||
import { Controller } from "react-hook-form";
|
||||
import { TiWarning } from "react-icons/ti";
|
||||
import { motion } from "framer-motion";
|
||||
import { AddIcon, CloseIcon } from "@chakra-ui/icons";
|
||||
import CurrencyInput from "./CurrencyInput";
|
||||
import ShariahLogo from "../../src/assets/shariah-icon.png"
|
||||
|
||||
const today = new Date().toISOString().split("T")[0];
|
||||
|
||||
@@ -69,12 +71,13 @@ const FormField = ({
|
||||
align,
|
||||
maxLength,
|
||||
dateValue,
|
||||
closingDate,
|
||||
...props
|
||||
}) => (
|
||||
<FormControl
|
||||
w={width ? width : "49%"}
|
||||
isInvalid={errors[name]}
|
||||
isRequired={type === "date" ? false: isRequired}
|
||||
isRequired={type === "date" ? true: isRequired}
|
||||
mb={2}
|
||||
>
|
||||
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
|
||||
@@ -84,6 +87,7 @@ const FormField = ({
|
||||
control={control}
|
||||
name={name}
|
||||
defaultValue={value}
|
||||
|
||||
rules={rules}
|
||||
render={({ field }) => {
|
||||
if (type === "select") {
|
||||
@@ -404,8 +408,7 @@ const FormField = ({
|
||||
w={6}
|
||||
h={6}
|
||||
src={
|
||||
" https://tanami.betadelivery.com/" +
|
||||
item?.logo
|
||||
import.meta.env.VITE_IMAGE_URL+item?.logo
|
||||
}
|
||||
/>
|
||||
{item.country === "United Arab Emirates"
|
||||
@@ -465,7 +468,7 @@ const FormField = ({
|
||||
} else if(type === 'date'){
|
||||
return (
|
||||
<Input
|
||||
position={'relative'}
|
||||
position={'relative'}
|
||||
bg={"#F5F8F6"}
|
||||
focusBorderColor="forestGreen.300"
|
||||
size={"sm"}
|
||||
@@ -477,12 +480,12 @@ const FormField = ({
|
||||
placeholder={placeHolder ? placeHolder : label}
|
||||
textAlign={arabic ? "right" : align ? align : "left"}
|
||||
_placeholder={{ fontSize: "sm" }}
|
||||
min={type === "date" ? today : undefined}
|
||||
maxLength={maxLength}
|
||||
// min={type === "date" ? today : undefined}
|
||||
// maxLength={maxLength}
|
||||
// defaultValue={type === "date" && "2023-07-26" : undefined}
|
||||
// defaultValue={value}
|
||||
// value={dateValue}
|
||||
|
||||
min={closingDate ? new Date().toISOString().split("T")[0] : undefined}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -506,7 +509,21 @@ const FormField = ({
|
||||
// value={dateValue}
|
||||
|
||||
/>
|
||||
);} else{
|
||||
);}
|
||||
else if(type === 'checkBox'){
|
||||
return (
|
||||
<HStack bg={"#F5F8F6"} p={"3px"} rounded={"2px"} border={'1px solid #E1E7EF'} justifyContent={"space-between"} mt={3}>
|
||||
{/* <Image w={"38px"} aspectRatio={"1/1"} src={ShariahLogo} /> */}
|
||||
<Checkbox
|
||||
// isChecked={value}
|
||||
isChecked={field.value}
|
||||
ps={1}
|
||||
{...field}
|
||||
{...props} size='md' colorScheme='forestGreen'>
|
||||
<Text as={"span"} fontSize={"sm"}>Is This Shariah Compliant</Text>
|
||||
</Checkbox>
|
||||
</HStack>
|
||||
);} else{
|
||||
return (
|
||||
<Input
|
||||
bg={"#F5F8F6"}
|
||||
|
||||
@@ -60,7 +60,8 @@ const FormInputMain = ({
|
||||
handleInputChange,
|
||||
align,
|
||||
maxLength,
|
||||
dateValue
|
||||
dateValue,
|
||||
closingDate
|
||||
},
|
||||
key
|
||||
) => (
|
||||
@@ -89,6 +90,7 @@ const FormInputMain = ({
|
||||
align={align}
|
||||
maxLength={maxLength}
|
||||
dateValue={dateValue}
|
||||
closingDate={closingDate}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
Checkbox, // Import Checkbox from Chakra UI
|
||||
} from "@chakra-ui/react";
|
||||
import React from "react";
|
||||
|
||||
@@ -34,8 +35,13 @@ const FormInputView = ({
|
||||
<Heading as="h6" size="xs" mt={index === 0 ? 3 : 4}>
|
||||
{section}
|
||||
</Heading>
|
||||
{/* <Box display={"flex"} gap={0}> */}
|
||||
<Box key={index} width={"100%"} display={"flex"} flexWrap={"wrap"} gap={4}>
|
||||
<Box
|
||||
key={index}
|
||||
width={"100%"}
|
||||
display={"flex"}
|
||||
flexWrap={"wrap"}
|
||||
gap={4}
|
||||
>
|
||||
{fields.map(
|
||||
({ value, label, id, width, btn, arabic, type, align }, key) =>
|
||||
type === "table" ? (
|
||||
@@ -62,8 +68,7 @@ const FormInputView = ({
|
||||
w={6}
|
||||
h={6}
|
||||
src={
|
||||
" https://tanami.betadelivery.com/" +
|
||||
item?.logo
|
||||
import.meta.env.VITE_IMAGE_URL + item?.logo
|
||||
}
|
||||
/>
|
||||
{item.country === "United Arab Emirates"
|
||||
@@ -94,7 +99,6 @@ const FormInputView = ({
|
||||
isRequired={true}
|
||||
bg={"#F5F8F6"}
|
||||
focusBorderColor="forestGreen.300"
|
||||
// border="1px solid #000"
|
||||
size={"sm"}
|
||||
fontSize={"sm"}
|
||||
rounded={"sm"}
|
||||
@@ -102,7 +106,6 @@ const FormInputView = ({
|
||||
value={item.value}
|
||||
textAlign={"right"}
|
||||
placeholder={"00.00"}
|
||||
// color={"#000"}
|
||||
color={"#1A202C"}
|
||||
fontWeight={500}
|
||||
border={"1px solid #e2e8f0"}
|
||||
@@ -120,7 +123,30 @@ const FormInputView = ({
|
||||
</Tr>
|
||||
</Tbody>
|
||||
</Table>
|
||||
) : type === "checkBox" ? (
|
||||
// <Box key={id} w={!width ? "49%" : width}>
|
||||
|
||||
<InputGroup
|
||||
display={"flex"}
|
||||
flexDirection={"column"}
|
||||
width={"32%"}
|
||||
size="sm"
|
||||
>
|
||||
<FormLabel key={id} color={"gray.500"} fontSize={"xs"}>
|
||||
{label}
|
||||
</FormLabel>
|
||||
<Checkbox
|
||||
isChecked={value}
|
||||
colorScheme="green"
|
||||
size="md"
|
||||
fontSize="sm"
|
||||
fontWeight="medium"
|
||||
>
|
||||
Is this shariah compliant
|
||||
</Checkbox>
|
||||
</InputGroup>
|
||||
) : (
|
||||
// </Box>
|
||||
<Box key={id} w={!width ? "49%" : width}>
|
||||
<FormLabel key={id} color={"gray.500"} fontSize={"xs"}>
|
||||
{label}
|
||||
@@ -141,7 +167,6 @@ const FormInputView = ({
|
||||
)
|
||||
)}
|
||||
</Box>
|
||||
{/* </Box> */}
|
||||
{index <
|
||||
Object.entries(groupedFields, groupedFieldsTwo).length - 1 && (
|
||||
<Divider />
|
||||
|
||||
@@ -13,16 +13,18 @@ import {
|
||||
Portal,
|
||||
Text,
|
||||
useColorMode,
|
||||
useDisclosure,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext } from "react";
|
||||
import React, { useContext, useRef } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { IoMdDownload } from "react-icons/io";
|
||||
import * as XLSX from "xlsx";
|
||||
import profile from "../assets/proavatar.webp";
|
||||
import GlobalStateContext from "../Contexts/GlobalStateContext";
|
||||
import { MdOutlineDarkMode, MdOutlineLightMode } from "react-icons/md";
|
||||
import logoMini from "../assets/propic.png"
|
||||
import logoMini from "../assets/propic.png";
|
||||
import { BsBack } from "react-icons/bs";
|
||||
import ChangePassword from "../Pages/ChangePassword";
|
||||
|
||||
const HeaderMain = ({
|
||||
link,
|
||||
@@ -31,10 +33,12 @@ const HeaderMain = ({
|
||||
icon,
|
||||
logOutHandler,
|
||||
slideDirecttion,
|
||||
data,
|
||||
}) => {
|
||||
const navigate = useNavigate()
|
||||
const navigate = useNavigate();
|
||||
const { colorMode, toggleColorMode } = useContext(GlobalStateContext);
|
||||
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const firstField = useRef();
|
||||
|
||||
return (
|
||||
<Box
|
||||
@@ -46,32 +50,31 @@ const HeaderMain = ({
|
||||
zIndex={999}
|
||||
>
|
||||
<HStack>
|
||||
{/* <ArrowBackIcon onClick={()=>navigate(-1)} /> */}
|
||||
<Text
|
||||
as={"span"}
|
||||
fontWeight={"500"}
|
||||
// color={"forestGreen.500"}
|
||||
className="fs-6 "
|
||||
>
|
||||
{/* <icon /> */}
|
||||
{title}
|
||||
</Text>
|
||||
{/* <ArrowBackIcon onClick={()=>navigate(-1)} /> */}
|
||||
<Text
|
||||
as={"span"}
|
||||
fontWeight={"500"}
|
||||
// color={"forestGreen.500"}
|
||||
className="fs-6 "
|
||||
>
|
||||
{/* <icon /> */}
|
||||
{title}
|
||||
</Text>
|
||||
</HStack>
|
||||
|
||||
|
||||
<Box me={4} gap={2} className="d-flex justify-content-center ">
|
||||
<Popover placement="bottom">
|
||||
<Portal>
|
||||
<PopoverContent maxW="200px" className="">
|
||||
<PopoverArrow />
|
||||
<PopoverBody className="web-text-medium pointer link">
|
||||
<PopoverBody onClick={()=> navigate('/profile')} className="web-text-medium pointer link">
|
||||
Profile
|
||||
</PopoverBody>
|
||||
<Link to={"/help-and-support"}>
|
||||
<Box onClick={onOpen}>
|
||||
<PopoverBody className="web-text-medium pointer ">
|
||||
Help & Support
|
||||
Change Password
|
||||
</PopoverBody>
|
||||
</Link>
|
||||
</Box>
|
||||
<PopoverFooter
|
||||
onClick={logOutHandler}
|
||||
className="web-text-medium pointer link"
|
||||
@@ -90,7 +93,7 @@ const HeaderMain = ({
|
||||
boxSize={37}
|
||||
name="Tanami M"
|
||||
src={logoMini}
|
||||
bg={'green.100'}
|
||||
bg={"green.100"}
|
||||
// p={1}
|
||||
/>
|
||||
<Box
|
||||
@@ -100,10 +103,10 @@ const HeaderMain = ({
|
||||
className=" overflow-hidden ms-3 flex-column "
|
||||
>
|
||||
<Text as={"span"} className="web-text-small">
|
||||
Hello, Tanami admin
|
||||
Hello, {data?.data?.firstName} {data?.data?.lastName}
|
||||
</Text>
|
||||
<Text as={"span"} className="web-text-xsmall">
|
||||
admin@tanami.com
|
||||
{data?.data?.emailAddress}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -113,6 +116,11 @@ const HeaderMain = ({
|
||||
{/* <Box onClick={() => toggleColorMode()} as="span" p={2} rounded={'lg'} className="link pointer">
|
||||
{colorMode === "light"? <MdOutlineDarkMode /> :<MdOutlineLightMode />}
|
||||
</Box> */}
|
||||
<ChangePassword
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
firstField={firstField}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -26,7 +26,7 @@ const ImageViewer = ({ src, isOpen, onClose }) => {
|
||||
rounded={6}
|
||||
w={"100%"}
|
||||
h={"100%"}
|
||||
src={"https://tanami.betadelivery.com/" + src}
|
||||
src={import.meta.env.VITE_IMAGE_URL + src}
|
||||
/>
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { Box, Spinner, Text } from "@chakra-ui/react";
|
||||
import React from "react";
|
||||
import './FullscreenLoaders.css'
|
||||
import "./FullscreenLoaders.css";
|
||||
|
||||
const FullscreenLoaders = ({height}) => {
|
||||
const FullscreenLoaders = ({ height }) => {
|
||||
return (
|
||||
<Box
|
||||
display={"flex"}
|
||||
justifyContent={"center"}
|
||||
flexDirection={'column'}
|
||||
flexDirection={"column"}
|
||||
alignItems={"center"}
|
||||
w={"100%"}
|
||||
h={height ? height: "100vh"}
|
||||
h={height ? height : "100vh"}
|
||||
gap={4}
|
||||
><div className="dot-spinner">
|
||||
>
|
||||
{/* <div className="dot-spinner">
|
||||
<div className="dot-spinner__dot"></div>
|
||||
<div className="dot-spinner__dot"></div>
|
||||
<div className="dot-spinner__dot"></div>
|
||||
@@ -21,8 +22,17 @@ const FullscreenLoaders = ({height}) => {
|
||||
<div className="dot-spinner__dot"></div>
|
||||
<div className="dot-spinner__dot"></div>
|
||||
<div className="dot-spinner__dot"></div>
|
||||
</div>
|
||||
{/* <Text color='#004717' fontSize={'md'} fontWeight={500}>Loading...</Text> */}
|
||||
</div> */}
|
||||
{/* <Text color='#004717' fontSize={'md'} fontWeight={500}>Loading...</Text> */}
|
||||
{/* <div className="spinner-grow" style={{backgroundColor:"#004118"}} role="status" /> */}
|
||||
|
||||
<Spinner
|
||||
thickness="4px"
|
||||
speed="0.65s"
|
||||
emptyColor="#fff"
|
||||
color="#004118"
|
||||
size="lg"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,19 +1,31 @@
|
||||
import React from "react";
|
||||
import './FullscreenLoaders.css'
|
||||
import { Spinner } from "@chakra-ui/react";
|
||||
|
||||
const Loader01 = () => {
|
||||
return (
|
||||
// <div className="dot-spinner">
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// </div>
|
||||
|
||||
<div className="dot-spinner">
|
||||
<div className="dot-spinner__dot"></div>
|
||||
<div className="dot-spinner__dot"></div>
|
||||
<div className="dot-spinner__dot"></div>
|
||||
<div className="dot-spinner__dot"></div>
|
||||
<div className="dot-spinner__dot"></div>
|
||||
<div className="dot-spinner__dot"></div>
|
||||
<div className="dot-spinner__dot"></div>
|
||||
<div className="dot-spinner__dot"></div>
|
||||
</div>
|
||||
// <div className="dot-spinner">
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// <div className="dot-spinner__dot"></div>
|
||||
// </div>
|
||||
<Spinner color='green.900' />
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Progress,
|
||||
Spinner,
|
||||
Stack,
|
||||
Text,
|
||||
} from "@chakra-ui/react";
|
||||
@@ -25,20 +26,26 @@ import { IoBatteryHalf } from "react-icons/io5";
|
||||
import { BiWifi } from "react-icons/bi";
|
||||
import { useGetIOByIdQuery } from "../Services/io.service";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import FullscreenLoaders from "./Loaders/FullscreenLoaders";
|
||||
import { calculatePercentage, formatDate } from "../Constants/Constants";
|
||||
import { BsFileText } from "react-icons/bs";
|
||||
|
||||
const MobileView = ({ isOpen, onClose, finalRef }) => {
|
||||
const MobileView = ({ isOpen, onClose, finalRef, actionId }) => {
|
||||
const [time, setTime] = useState(new Date());
|
||||
const navigate = useNavigate();
|
||||
const params = useParams();
|
||||
const id = params?.id;
|
||||
const id = actionId;
|
||||
|
||||
const {
|
||||
data,
|
||||
data: IObyID,
|
||||
isLoading: IObyIDisLoading,
|
||||
error: IObyIDerror,
|
||||
} = useGetIOByIdQuery(id, { skip: !id });
|
||||
|
||||
console.log(data);
|
||||
console.log(IObyID);
|
||||
|
||||
const keyMerits = IObyID?.data?.keyMerits || [];
|
||||
const artifactsImage = IObyID?.data?.artifactsImage || [];
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
@@ -52,10 +59,17 @@ const MobileView = ({ isOpen, onClose, finalRef }) => {
|
||||
return date.toLocaleTimeString([], {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
hour12: true,
|
||||
});
|
||||
};
|
||||
|
||||
console.log(
|
||||
calculatePercentage(
|
||||
IObyID?.data?.totalAmtInvestmentInUSD,
|
||||
IObyID?.data?.goalAmount
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
display={"flex"}
|
||||
@@ -79,7 +93,17 @@ const MobileView = ({ isOpen, onClose, finalRef }) => {
|
||||
display={"flex"}
|
||||
justifyContent={"center"}
|
||||
h={"600px"}
|
||||
w={"330px"}
|
||||
w={"320px"}
|
||||
sx={{
|
||||
"@media (max-width: 2024px)": {
|
||||
height: "695px",
|
||||
width: "360px",
|
||||
},
|
||||
"@media (max-width: 1440px)": {
|
||||
height: "600px",
|
||||
width: "320px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
h={"100%"}
|
||||
@@ -98,278 +122,351 @@ const MobileView = ({ isOpen, onClose, finalRef }) => {
|
||||
pt={"36px"}
|
||||
px={"15px"}
|
||||
>
|
||||
<Box>
|
||||
{IObyIDisLoading ? (
|
||||
<Box
|
||||
display={"flex"}
|
||||
justifyContent={"center"}
|
||||
alignItems={"center"}
|
||||
position={"absolute"}
|
||||
left={"30px"}
|
||||
top={"18px"}
|
||||
h={"100%"}
|
||||
>
|
||||
<Text ml={1} mb={0}>
|
||||
<GiNetworkBars fontSize={"10px"} />
|
||||
</Text>
|
||||
<Text ml={1} mb={0} fontSize={"10px"}>
|
||||
{formatTime(time)}
|
||||
</Text>
|
||||
<Text ml={"5px"} mb={0}>
|
||||
<GrLinkedinOption fontSize={"10px"} />
|
||||
</Text>
|
||||
{/* <Text ml={1} mb={0}><FiInstagram fontSize={"10px"} /></Text> */}
|
||||
<Spinner thickness="3px" color="purple.900" size="lg" />
|
||||
</Box>
|
||||
<Box
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
position={"absolute"}
|
||||
right={"36px"}
|
||||
top={"17px"}
|
||||
>
|
||||
<Text mb={0}>
|
||||
<BiWifi fontSize={"14px"} />
|
||||
</Text>
|
||||
<Text ml={1} mb={0}>
|
||||
<IoBatteryHalf fontSize={"15px"} />
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
p={"10px"}
|
||||
overflowY={"scroll"}
|
||||
h={"483px"}
|
||||
zIndex={"99"}
|
||||
position={"relative"}
|
||||
borderBottomLeftRadius={"23px"}
|
||||
borderBottomRightRadius={"23px"}
|
||||
>
|
||||
<Box
|
||||
mb={4}
|
||||
bg={"#f5f8f6"}
|
||||
borderRadius={"20px"}
|
||||
boxShadow={"rgba(0, 0, 0, 0.15) 0px 2px 8px"}
|
||||
>
|
||||
<Box position={"relative"}>
|
||||
<Text
|
||||
position={"absolute"}
|
||||
top={"12px"}
|
||||
left={"10px"}
|
||||
backgroundColor={"#e4f6ea"}
|
||||
fontSize={"10px"}
|
||||
fontWeight={500}
|
||||
color="green"
|
||||
p={"7px 12px"}
|
||||
borderRadius={"20px"}
|
||||
>
|
||||
Stock
|
||||
</Text>
|
||||
<Text
|
||||
position={"absolute"}
|
||||
top={"12px"}
|
||||
right={"10px"}
|
||||
fontSize={"10px"}
|
||||
) : (
|
||||
<>
|
||||
<Box>
|
||||
<Box
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
fontWeight={500}
|
||||
backgroundColor={"#fff"}
|
||||
p={"7px 12px"}
|
||||
borderRadius={"20px"}
|
||||
position={"absolute"}
|
||||
left={"30px"}
|
||||
top={"18px"}
|
||||
>
|
||||
<LuClock color="#d8804e" />{" "}
|
||||
<Text mb={0} ml={1}>
|
||||
Closing Date Aug 23 2024
|
||||
<Text ml={1} mb={0}>
|
||||
<GiNetworkBars fontSize={"10px"} />
|
||||
</Text>
|
||||
</Text>
|
||||
<Image
|
||||
borderTopLeftRadius={"20px"}
|
||||
borderTopRightRadius={"20px"}
|
||||
h={"130px"}
|
||||
w={"100%"}
|
||||
src={mobileBanner}
|
||||
/>
|
||||
</Box>
|
||||
<Stack mt="3" bg={"#fff"} py={4} px={4}>
|
||||
<Text
|
||||
fontSize={"sm"}
|
||||
fontWeight={"500"}
|
||||
color={"#000"}
|
||||
mb={0}
|
||||
>
|
||||
Guinevere Gates
|
||||
</Text>
|
||||
<Heading fontSize="16px" color={"#004717"}>
|
||||
BHD 46,258
|
||||
</Heading>
|
||||
<Progress
|
||||
colorScheme="green"
|
||||
size="sm"
|
||||
value={20}
|
||||
borderRadius={"3px"}
|
||||
/>
|
||||
<Text
|
||||
color={"#4e4e4e"}
|
||||
fontSize={"xs"}
|
||||
fontWeight={600}
|
||||
mb={0}
|
||||
>
|
||||
0.0% funded
|
||||
</Text>
|
||||
<Text
|
||||
fontSize={"xs"}
|
||||
fontWeight={500}
|
||||
mb={0}
|
||||
color={"#9d9d9d"}
|
||||
>
|
||||
fugit eligendi dolore dolore et
|
||||
</Text>
|
||||
</Stack>
|
||||
<Box py={4} px={4}>
|
||||
<Box display={"flex"} justifyContent={"space-between"}>
|
||||
<Text
|
||||
fontSize={"10px"}
|
||||
mb={1}
|
||||
fontWeight={600}
|
||||
color={"#616161"}
|
||||
>
|
||||
Sponsor name:
|
||||
<Text ml={1} mb={0} fontSize={"10px"}>
|
||||
{formatTime(time)}
|
||||
</Text>
|
||||
<Text fontSize={"10px"} mb={1} fontWeight={600}>
|
||||
Scott Simon
|
||||
</Text>
|
||||
</Box>
|
||||
<Box display={"flex"} justifyContent={"space-between"}>
|
||||
<Text
|
||||
fontSize={"10px"}
|
||||
mb={1}
|
||||
fontWeight={600}
|
||||
color={"#616161"}
|
||||
>
|
||||
Estimated return:
|
||||
</Text>
|
||||
<Text fontSize={"10px"} mb={1} fontWeight={600}>
|
||||
A provident veniam
|
||||
</Text>
|
||||
</Box>
|
||||
<Box display={"flex"} justifyContent={"space-between"}>
|
||||
<Text
|
||||
fontSize={"10px"}
|
||||
mb={1}
|
||||
fontWeight={600}
|
||||
color={"#616161"}
|
||||
>
|
||||
Hoiding period:
|
||||
</Text>
|
||||
<Text fontSize={"10px"} mb={1} fontWeight={600}>
|
||||
Eius eiusmod exericit
|
||||
</Text>
|
||||
</Box>
|
||||
<Box display={"flex"} justifyContent={"space-between"}>
|
||||
<Text
|
||||
fontSize={"10px"}
|
||||
mb={1}
|
||||
fontWeight={600}
|
||||
color={"#616161"}
|
||||
>
|
||||
Minimum investment:
|
||||
</Text>
|
||||
<Text fontSize={"10px"} mb={1} fontWeight={600}>
|
||||
BHD 1
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
mb={4}
|
||||
p={5}
|
||||
bg={"#f5f8f6"}
|
||||
borderRadius={"20px"}
|
||||
boxShadow={"rgba(0, 0, 0, 0.15) 0px 2px 8px"}
|
||||
>
|
||||
<Heading fontSize="14px" fontWeight={600}>
|
||||
Key Merits
|
||||
</Heading>
|
||||
<Box display={"flex"} alignItems={"center"}>
|
||||
<Image
|
||||
width={"30px"}
|
||||
me={2}
|
||||
src="https://tanami.betadelivery.com/public/icons/icon3.svg"
|
||||
/>
|
||||
<Text fontSize={"xs"} mb={0}>
|
||||
Sit sunt consequunt Dolores minim suscip
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
mb={4}
|
||||
p={5}
|
||||
borderRadius={"20px"}
|
||||
boxShadow={"rgba(0, 0, 0, 0.15) 0px 2px 8px"}
|
||||
>
|
||||
<Heading fontSize="14px" fontWeight={600}>
|
||||
Investment Documents
|
||||
</Heading>
|
||||
<Box bg={"#f5f8f6"} w={"150px"} p={3} borderRadius={"10px"}>
|
||||
<Box display={"flex"} alignItems={"center"} mb={2}>
|
||||
<Image
|
||||
me={1}
|
||||
src="https://tanami.betadelivery.com/public/icons/icon8.svg"
|
||||
/>
|
||||
<Text fontSize={"xs"} mb={0}>
|
||||
Merrill Rocha
|
||||
<Text ml={"5px"} mb={0}>
|
||||
<GrLinkedinOption fontSize={"10px"} />
|
||||
</Text>
|
||||
{/* <Text ml={1} mb={0}><FiInstagram fontSize={"10px"} /></Text> */}
|
||||
</Box>
|
||||
<Box
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
justifyContent={"space-between"}
|
||||
position={"absolute"}
|
||||
right={"36px"}
|
||||
top={"17px"}
|
||||
>
|
||||
<Text mb={0} fontSize={"sm"}>
|
||||
0.03 mb
|
||||
<Text mb={0}>
|
||||
<BiWifi fontSize={"14px"} />
|
||||
</Text>
|
||||
<Text ml={1} mb={0}>
|
||||
<IoBatteryHalf fontSize={"15px"} />
|
||||
</Text>
|
||||
<GrDownload fontSize={"15px"} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
mb={4}
|
||||
p={4}
|
||||
borderRadius={"20px"}
|
||||
boxShadow={"rgba(0, 0, 0, 0.15) 0px 2px 8px"}
|
||||
>
|
||||
<Heading fontSize="14px" fontWeight={600}>
|
||||
Videos
|
||||
</Heading>
|
||||
<video autoPlay style={{ borderRadius: "18px" }}>
|
||||
<source
|
||||
src="https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4"
|
||||
type="video/mp4"
|
||||
style={{ height: "200px" }}
|
||||
/>
|
||||
</video>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
p={"10px"}
|
||||
overflowY={"scroll"}
|
||||
h={"483px"}
|
||||
zIndex={"99"}
|
||||
position={"relative"}
|
||||
borderBottomLeftRadius={"23px"}
|
||||
borderBottomRightRadius={"23px"}
|
||||
sx={{
|
||||
"@media (max-width: 2024px)": {
|
||||
height: "575px",
|
||||
},
|
||||
"@media (max-width: 1440px)": {
|
||||
height: "483px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
mb={4}
|
||||
bg={"#f5f8f6"}
|
||||
borderRadius={"20px"}
|
||||
boxShadow={"rgba(0, 0, 0, 0.15) 0px 2px 8px"}
|
||||
>
|
||||
<Box position={"relative"}>
|
||||
<Text
|
||||
position={"absolute"}
|
||||
top={"12px"}
|
||||
left={"10px"}
|
||||
backgroundColor={"#e4f6ea"}
|
||||
fontSize={"10px"}
|
||||
fontWeight={500}
|
||||
color="green"
|
||||
p={"7px 12px"}
|
||||
borderRadius={"20px"}
|
||||
>
|
||||
Stock
|
||||
</Text>
|
||||
<Text
|
||||
position={"absolute"}
|
||||
top={"12px"}
|
||||
right={"10px"}
|
||||
fontSize={"10px"}
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
fontWeight={500}
|
||||
backgroundColor={"#fff"}
|
||||
p={"7px 12px"}
|
||||
borderRadius={"20px"}
|
||||
>
|
||||
<LuClock color="#d8804e" />{" "}
|
||||
<Text mb={0} ml={1}>
|
||||
Closing Date {formatDate(IObyID?.data?.closingDate)}
|
||||
</Text>
|
||||
</Text>
|
||||
{artifactsImage?.[0]?.artifactPathName && (
|
||||
<Image
|
||||
borderTopLeftRadius={"20px"}
|
||||
borderTopRightRadius={"20px"}
|
||||
h={"130px"}
|
||||
w={"100%"}
|
||||
src={
|
||||
import.meta.env.VITE_IMAGE_URL+
|
||||
artifactsImage[0]?.artifactPathName
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<Stack mt="3" bg={"#fff"} py={4} px={4}>
|
||||
<Text
|
||||
fontSize={"sm"}
|
||||
fontWeight={"500"}
|
||||
color={"#000"}
|
||||
mb={0}
|
||||
>
|
||||
{IObyID?.data?.investmentType?.investmentTypeName}
|
||||
</Text>
|
||||
<Heading fontSize="16px" color={"#004717"}>
|
||||
BHD {IObyID?.data?.goalAmount}
|
||||
</Heading>
|
||||
<Progress
|
||||
colorScheme="green"
|
||||
size="sm"
|
||||
value={calculatePercentage(
|
||||
IObyID?.data?.totalAmtInvestmentInUSD,
|
||||
IObyID?.data?.goalAmount
|
||||
)}
|
||||
borderRadius={"3px"}
|
||||
/>
|
||||
<Text
|
||||
color={"#4e4e4e"}
|
||||
fontSize={"xs"}
|
||||
fontWeight={600}
|
||||
mb={0}
|
||||
>
|
||||
{parseFloat(
|
||||
calculatePercentage(
|
||||
IObyID?.data?.totalAmtInvestmentInUSD,
|
||||
IObyID?.data?.goalAmount
|
||||
) || 0
|
||||
).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
% funded
|
||||
</Text>
|
||||
<Text
|
||||
fontSize={"xs"}
|
||||
fontWeight={500}
|
||||
mb={0}
|
||||
color={"#9d9d9d"}
|
||||
>
|
||||
{IObyID?.data?.descriptionEnglish}
|
||||
</Text>
|
||||
</Stack>
|
||||
<Box py={4} px={4}>
|
||||
<Box display={"flex"} justifyContent={"space-between"}>
|
||||
<Text
|
||||
fontSize={"xs"}
|
||||
mb={1}
|
||||
fontWeight={500}
|
||||
color={"#616161"}
|
||||
>
|
||||
Sponsor name:
|
||||
</Text>
|
||||
<Text fontSize={"xs"} mb={1} fontWeight={500}>
|
||||
{IObyID?.data?.sponsor?.sponsorName}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box display={"flex"} justifyContent={"space-between"}>
|
||||
<Text
|
||||
fontSize={"xs"}
|
||||
mb={1}
|
||||
fontWeight={500}
|
||||
color={"#616161"}
|
||||
>
|
||||
Estimated return:
|
||||
</Text>
|
||||
<Text fontSize={"xs"} mb={1} fontWeight={500}>
|
||||
{IObyID?.data?.expectedReturn}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box display={"flex"} justifyContent={"space-between"}>
|
||||
<Text
|
||||
fontSize={"xs"}
|
||||
mb={1}
|
||||
fontWeight={500}
|
||||
color={"#616161"}
|
||||
>
|
||||
Hoiding period:
|
||||
</Text>
|
||||
<Text fontSize={"xs"} mb={1} fontWeight={500}>
|
||||
{IObyID?.data?.holdingPeriod}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box display={"flex"} justifyContent={"space-between"}>
|
||||
<Text
|
||||
fontSize={"xs"}
|
||||
mb={1}
|
||||
fontWeight={500}
|
||||
color={"#616161"}
|
||||
>
|
||||
Minimum investment:
|
||||
</Text>
|
||||
<Text fontSize={"xs"} mb={1} fontWeight={500}>
|
||||
{
|
||||
IObyID?.data?.minInvestmentAmt?.[0]?.country
|
||||
?.minInvestmentAmt
|
||||
}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
mb={4}
|
||||
p={5}
|
||||
bg={"#f5f8f6"}
|
||||
borderRadius={"20px"}
|
||||
boxShadow={"rgba(0, 0, 0, 0.15) 0px 2px 8px"}
|
||||
>
|
||||
<Heading fontSize="14px" fontWeight={600}>
|
||||
Key Merits
|
||||
</Heading>
|
||||
<Box>
|
||||
{keyMerits &&
|
||||
keyMerits.map((item, index) => (
|
||||
<Box display={"flex"} alignItems={"center"}>
|
||||
<Image
|
||||
rounded={"md"}
|
||||
display={"flex"}
|
||||
p={1}
|
||||
justifyContent={"center"}
|
||||
alignItems={"center"}
|
||||
src={
|
||||
import.meta.env.VITE_IMAGE_URL+
|
||||
item?.icon.iconFilePath
|
||||
}
|
||||
w={8}
|
||||
h={8}
|
||||
/>
|
||||
<Text fontSize={"xs"} mb={0}>
|
||||
{item?.meritsDescription}
|
||||
</Text>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
mb={4}
|
||||
p={5}
|
||||
borderRadius={"20px"}
|
||||
boxShadow={"rgba(0, 0, 0, 0.15) 0px 2px 8px"}
|
||||
>
|
||||
<Heading fontSize="14px" fontWeight={600}>
|
||||
Investment Documents
|
||||
</Heading>
|
||||
<Box
|
||||
bg={"#f5f8f6"}
|
||||
w={"150px"}
|
||||
p={3}
|
||||
borderRadius={"10px"}
|
||||
>
|
||||
<Box display={"flex"} alignItems={"center"} mb={2}>
|
||||
{/* <Image
|
||||
me={1}
|
||||
src="https://tanami.betadelivery.com/public/icons/icon8.svg"
|
||||
/> */}
|
||||
<BsFileText color="forestGreen" fontSize="18px" />
|
||||
<Text fontSize={"xs"} mb={0} ml={2}>
|
||||
{IObyID?.data?.documents?.[0]?.documentName}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
justifyContent={"space-between"}
|
||||
>
|
||||
<Text mb={0} fontSize={"sm"}>
|
||||
{IObyID?.data?.documents?.[0]?.documentSize}
|
||||
</Text>
|
||||
<GrDownload fontSize={"15px"} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
mb={4}
|
||||
p={4}
|
||||
borderRadius={"20px"}
|
||||
boxShadow={"rgba(0, 0, 0, 0.15) 0px 2px 8px"}
|
||||
>
|
||||
<Heading fontSize="14px" fontWeight={600}>
|
||||
Videos
|
||||
</Heading>
|
||||
<video
|
||||
autoPlay
|
||||
loop
|
||||
controls
|
||||
style={{
|
||||
borderRadius: "18px",
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
}}
|
||||
>
|
||||
<source
|
||||
src={
|
||||
import.meta.env.VITE_IMAGE_URL+IObyID?.data?.artifactsVideo?.[0]
|
||||
?.artifactStreamingURL
|
||||
}
|
||||
type="video/mp4"
|
||||
style={{ height: "200px" }}
|
||||
/>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
position={"relative"}
|
||||
p={4}
|
||||
background={"#fff"}
|
||||
padding={"24px"}
|
||||
paddingBottom={"3px"}
|
||||
borderBottomLeftRadius={"30px"}
|
||||
borderBottomRightRadius="30px"
|
||||
>
|
||||
<Button
|
||||
margin={"auto"}
|
||||
width={"85%"}
|
||||
bottom="10px"
|
||||
left="0"
|
||||
colorScheme="forestGreen"
|
||||
mr={3}
|
||||
w={"100%"}
|
||||
fontWeight={500}
|
||||
borderRadius={"20px"}
|
||||
>
|
||||
Invest
|
||||
</Button>
|
||||
</Box>
|
||||
<Box
|
||||
position={"relative"}
|
||||
p={4}
|
||||
background={"#fff"}
|
||||
padding={"24px"}
|
||||
paddingBottom={"3px"}
|
||||
borderBottomLeftRadius={"30px"}
|
||||
borderBottomRightRadius="30px"
|
||||
>
|
||||
<Button
|
||||
margin={"auto"}
|
||||
width={"85%"}
|
||||
bottom="10px"
|
||||
left="0"
|
||||
colorScheme="forestGreen"
|
||||
mr={3}
|
||||
w={"100%"}
|
||||
fontWeight={500}
|
||||
borderRadius={"20px"}
|
||||
>
|
||||
Invest
|
||||
</Button>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</HStack>
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Select, HStack, Text, Box, IconButton } from '@chakra-ui/react';
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from '@chakra-ui/icons';
|
||||
import React, { useState } from "react";
|
||||
import { Select, HStack, Text, Box, IconButton } from "@chakra-ui/react";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "@chakra-ui/icons";
|
||||
|
||||
const Pagination = ({ pageSize, setPageSize, totalItems,isLoading, setCurrentPage, currentPage }) => {
|
||||
const Pagination = ({
|
||||
pageSize,
|
||||
setPageSize,
|
||||
totalItems = 1,
|
||||
isLoading,
|
||||
setCurrentPage,
|
||||
currentPage,
|
||||
}) => {
|
||||
// const [] = useState(itemsPerPageOptions[0]);
|
||||
|
||||
|
||||
const totalPages = Math.ceil(totalItems / pageSize);
|
||||
|
||||
const handlePageSizeChange = (e) => {
|
||||
@@ -35,45 +41,54 @@ const Pagination = ({ pageSize, setPageSize, totalItems,isLoading, setCurrentPag
|
||||
{/* <Text className='web-text-small'>Tanami v0.1</Text> */}
|
||||
|
||||
<HStack>
|
||||
<Select
|
||||
|
||||
className="pointer web-text-small"
|
||||
width={"90px"}
|
||||
rounded="sm"
|
||||
size="sm"
|
||||
value={pageSize}
|
||||
onChange={handlePageSizeChange}
|
||||
>
|
||||
{[ 15, 20, 30]?.map((size) => (
|
||||
<option key={size} value={size}>
|
||||
{size}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
<Select
|
||||
className="pointer web-text-small"
|
||||
width={"90px"}
|
||||
rounded="sm"
|
||||
size="sm"
|
||||
value={pageSize}
|
||||
onChange={handlePageSizeChange}
|
||||
>
|
||||
{[15, 20, 30, 500]?.map((size) => (
|
||||
<option key={size} value={size}>
|
||||
{size}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
<IconButton
|
||||
mt={1}
|
||||
size={'sm'}
|
||||
rounded="sm"
|
||||
mt={1}
|
||||
size={"sm"}
|
||||
rounded="sm"
|
||||
icon={<ChevronLeftIcon />}
|
||||
onClick={paginationPrev}
|
||||
className="link pointer"
|
||||
isDisabled={currentPage === 1}
|
||||
aria-label="Previous Page"
|
||||
/>
|
||||
<Text w={"81px"} display={'flex'} justifyContent={'center'} className="web-text-medium" as={"span"}>
|
||||
{isLoading ? "0": displayRange?.start} - {isLoading ? "00" :displayRange?.end} of {isLoading ? "00":totalItems}
|
||||
<Text
|
||||
w={"81px"}
|
||||
display={"flex"}
|
||||
justifyContent={"center"}
|
||||
className="web-text-medium"
|
||||
as={"span"}
|
||||
>
|
||||
{isLoading ? "0" : displayRange?.start} -{" "}
|
||||
{isLoading ? "00" : displayRange?.end} of{" "}
|
||||
{isLoading ? "00" : totalItems}
|
||||
</Text>
|
||||
<IconButton
|
||||
mt={1}
|
||||
mt={1}
|
||||
icon={<ChevronRightIcon />}
|
||||
size={'sm'}
|
||||
rounded="sm"
|
||||
size={"sm"}
|
||||
rounded="sm"
|
||||
onClick={paginationNext}
|
||||
className="link pointer"
|
||||
isDisabled={currentPage === totalPages}
|
||||
aria-label="Next Page"
|
||||
/>
|
||||
</HStack>
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
||||
export default Pagination;
|
||||
export default Pagination;
|
||||
|
||||
202
src/Components/Popups/ConfirmReversalPopups.jsx
Normal 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;
|
||||
133
src/Components/Popups/InitiateReversalPopups.jsx
Normal 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;
|
||||
132
src/Components/Popups/RejectReversalPopups.jsx
Normal 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;
|
||||
65
src/Components/RoleSwitchButton.jsx
Normal 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;
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CheckCircleIcon, WarningIcon } from "@chakra-ui/icons";
|
||||
import { Box, Text } from "@chakra-ui/react";
|
||||
import React from "react";
|
||||
import { PiWarningBold } from "react-icons/pi";
|
||||
|
||||
const ToastBox = ({ message, status }) => {
|
||||
return (
|
||||
@@ -9,10 +10,10 @@ const ToastBox = ({ message, status }) => {
|
||||
rounded={"sm"}
|
||||
className="web-text-large d-flex gap-2 align-items-center"
|
||||
p={3}
|
||||
bg={status === "error" ? "red.500" : status === "warn" ? "blue.500" : "green.500"}
|
||||
bg={status === "error" ? "red.500" : status === "warn" ? "yellow.500" : status === "info" ? "blue.500" : "green.500"}
|
||||
>
|
||||
|
||||
{status === "error" || status === "warn" ? <WarningIcon/> : <CheckCircleIcon /> }
|
||||
{status === "error" || status === "warn" ? <PiWarningBold/> : status === "info" ? <WarningIcon/> : <CheckCircleIcon /> }
|
||||
<Text as={"span"}>{message}</Text>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
|
||||
import dns from "node:dns"
|
||||
import * as XLSX from 'xlsx';
|
||||
import CryptoJS from "crypto-js";
|
||||
|
||||
|
||||
|
||||
export const generateSerialNumber = (index, currentPage = 1, pageSize = 1) => {
|
||||
return (currentPage - 1) * pageSize + (index + 1);
|
||||
};
|
||||
|
||||
export function getTomorrowDate() {
|
||||
const today = new Date();
|
||||
const tomorrow = new Date(today);
|
||||
tomorrow.setDate(today.getDate() + 1);
|
||||
|
||||
|
||||
// Format the date as YYYY-MM-DD (ISO 8601)
|
||||
return tomorrow.toISOString().split('T')[0];
|
||||
}
|
||||
@@ -30,7 +33,7 @@ export function removeTrailingZeros(value) {
|
||||
}
|
||||
|
||||
|
||||
export function getCountdownTimer(utcDateString) {
|
||||
export function getCountdownTimer(utcDateString) {
|
||||
// Parse the UTC datetime string into a Date object
|
||||
const targetDate = new Date(utcDateString);
|
||||
const now = new Date();
|
||||
@@ -53,10 +56,14 @@ export function removeTrailingZeros(value) {
|
||||
const remainingMinutes = minutes % 60;
|
||||
const remainingSeconds = seconds % 60;
|
||||
|
||||
return `${remainingDays === 0 ? "": remainingDays+"d"} ${remainingHours === 0 ? "": remainingHours+"h"} ${remainingMinutes}m `;
|
||||
return `${remainingDays === 0 ? "" : remainingDays + "d"} ${remainingHours === 0 ? "" : remainingHours + "h"} ${remainingMinutes}m ${remainingSeconds}s `;
|
||||
}
|
||||
|
||||
|
||||
export function bytesToMB(bytes) {
|
||||
return (bytes / (1024 * 1024)).toFixed(2); // Convert bytes to MB and limit to 2 decimal places
|
||||
}
|
||||
|
||||
|
||||
export function startCountdown(utcDateString) {
|
||||
// Function to update the countdown
|
||||
@@ -87,30 +94,33 @@ export function debounce(func, delay) {
|
||||
|
||||
|
||||
|
||||
async function resolveMx(domain, recordType) {
|
||||
async function resolveMx(domain) {
|
||||
return new Promise((resolve, reject) => {
|
||||
dns.resolveMx(domain, (err, mxRecords) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
const addresses = mxRecords.map((mxRecord) => mxRecord.exchange);
|
||||
resolve(addresses);
|
||||
});
|
||||
dns.resolveMx(domain, (err, mxRecords) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
const addresses = mxRecords.map((mxRecord) => mxRecord.exchange);
|
||||
resolve(addresses);
|
||||
});
|
||||
});
|
||||
}
|
||||
// Async function to check email address validity
|
||||
export async function checkEmailValidity(email) {
|
||||
try {
|
||||
const domain = email?.split("@")[1];
|
||||
const addresses = await resolveMx(domain, "MX");
|
||||
const domain = email?.split('@')[1];
|
||||
const addresses = await resolveMx(domain, 'MX');
|
||||
console.log(addresses);
|
||||
|
||||
if (addresses && addresses?.length > 0) {
|
||||
return true;
|
||||
}
|
||||
return false; // No MX record exists
|
||||
if (addresses && addresses?.length > 0) {
|
||||
return true;
|
||||
}
|
||||
return false; // No MX record exists
|
||||
} catch (err) {
|
||||
return false; // Error occurred
|
||||
console.log(err);
|
||||
|
||||
return false; // Error occurred
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,15 +128,118 @@ export async function checkEmailValidity(email) {
|
||||
// Function to convert timestamp to readable date format in Gulf timezone
|
||||
export function formatTimestampInGulfTimezone(timestamp) {
|
||||
const date = new Date(timestamp);
|
||||
const options = {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
timeZone: 'Asia/Dubai', // Gulf Standard Time (GST) timezone
|
||||
timeZoneName: 'short'
|
||||
const options = {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
timeZone: 'Asia/Dubai', // Gulf Standard Time (GST) timezone
|
||||
timeZoneName: 'short'
|
||||
};
|
||||
return date.toLocaleDateString('en-GB', options);
|
||||
}
|
||||
|
||||
|
||||
export function formatDate(dateString) {
|
||||
const options = { year: 'numeric', month: 'short', day: 'numeric' };
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleDateString('en-US', options);
|
||||
}
|
||||
|
||||
export function calculatePercentage(part, total) {
|
||||
if (total === 0) {
|
||||
return 0; // To avoid division by zero
|
||||
}
|
||||
return (part / total) * 100;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const getNestedValue = (obj, key) => {
|
||||
return key.split('.').reduce((value, part) => {
|
||||
return value && value[part] ? value[part] : null;
|
||||
}, obj);
|
||||
};
|
||||
|
||||
export const exportToExcel = (data, headers) => {
|
||||
const flattenedData = data.map((item) => {
|
||||
const newItem = {};
|
||||
|
||||
// Loop through customHeaders and get the correct values
|
||||
headers.forEach((header) => {
|
||||
newItem[header.label] = getNestedValue(item, header.key); // Use the helper function
|
||||
});
|
||||
|
||||
return newItem; // Return the new flat object
|
||||
});
|
||||
|
||||
// Now pass flattenedData to your Excel library to generate the file
|
||||
// Assuming you're using a library like `xlsx` for this part:
|
||||
|
||||
const worksheet = XLSX.utils.json_to_sheet(flattenedData);
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
|
||||
|
||||
// Generate file
|
||||
XLSX.writeFile(workbook, "exported_data.xlsx");
|
||||
};
|
||||
|
||||
|
||||
export const exportToExcelNew = (data, fileName = "exported_data.xlsx") => {
|
||||
console.log("Data to export:", data); // Log the data for debugging
|
||||
|
||||
// Ensure the data is not empty
|
||||
if (!data || data.length === 0) {
|
||||
console.error("No data provided for export.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert the data to a worksheet
|
||||
const worksheet = XLSX.utils.json_to_sheet(data);
|
||||
|
||||
// Create a new workbook and append the worksheet to it
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
|
||||
|
||||
// Ensure file has a valid .xlsx extension
|
||||
const fileWithExtension = fileName.endsWith(".xlsx") ? fileName : `${fileName}.xlsx`;
|
||||
|
||||
// Write the workbook to a file
|
||||
XLSX.writeFile(workbook, fileWithExtension);
|
||||
};
|
||||
|
||||
|
||||
export function formatDateToYYYYMMDD(dateString) {
|
||||
const date = new Date(dateString);
|
||||
|
||||
// Extract individual date components
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are 0-based
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
|
||||
// Combine the formatted parts
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
|
||||
// Encrypt a string
|
||||
export const encryptString = (text) => {
|
||||
const ciphertext = CryptoJS.AES.encrypt(text, import.meta.env.VITE_ROLE_ENCRYPTION_KEY).toString();
|
||||
return ciphertext;
|
||||
};
|
||||
|
||||
// Decrypt a string
|
||||
export const decryptString = (ciphertext) => {
|
||||
const bytes = CryptoJS.AES.decrypt(ciphertext, import.meta.env.VITE_ROLE_ENCRYPTION_KEY);
|
||||
const originalText = bytes.toString(CryptoJS.enc.Utf8);
|
||||
return originalText;
|
||||
};
|
||||
|
||||
export const SUPER_ADMIN_ID = Number(import.meta.env.VITE_SUPER_ADMIN_ID) || 1
|
||||
export const MAKER_ID = import.meta.env.VITE_MAKER_ID || 1
|
||||
export const CHECKER_ID = import.meta.env.VITE_CHECKER_ID || 2
|
||||
|
||||
export const isMaker = (role = decryptString(localStorage?.getItem("role"))) => role === import.meta.env.VITE_MAKER;
|
||||
export const isChecker = (role = decryptString(localStorage?.getItem("role"))) => role === import.meta.env.VITE_CHECKER;
|
||||
|
||||
@@ -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 }
|
||||
@@ -6,7 +6,7 @@ import { v4 as uuidv4 } from "uuid";
|
||||
import { TbClock2 } from "react-icons/tb";
|
||||
import { CiWallet } from "react-icons/ci";
|
||||
import { HiOutlineReceiptPercent } from "react-icons/hi2";
|
||||
import { IoMdQrScanner } from "react-icons/io";
|
||||
import { IoMdQrScanner } from "react-icons/io";
|
||||
import { GrDocumentPdf } from "react-icons/gr";
|
||||
import { AiOutlineFileGif } from "react-icons/ai";
|
||||
|
||||
@@ -519,7 +519,7 @@ const GlobalStateProvider = ({ children }) => {
|
||||
rate: 2.66,
|
||||
},
|
||||
]);
|
||||
const [InvestorDetails, setInvestorDetails] = useState([
|
||||
const [InvestorDetails, setInvestorDetails] = useState([
|
||||
{
|
||||
id: 1,
|
||||
clientId: "SA00000001",
|
||||
@@ -530,7 +530,7 @@ const GlobalStateProvider = ({ children }) => {
|
||||
phoneNumber: "8940035906",
|
||||
address: "Saudi Arabia",
|
||||
emailID: "john@gmail.com",
|
||||
InvestorType:"Retail",
|
||||
InvestorType: "Retail",
|
||||
bankName: "Lorem Text",
|
||||
branchAddress: "Hohenzollernring 58, 95444",
|
||||
iban: "DE 1234 5678 9012 3456",
|
||||
@@ -549,7 +549,7 @@ const GlobalStateProvider = ({ children }) => {
|
||||
phoneNumber: "8940035906",
|
||||
address: "Saudi Arabia",
|
||||
emailID: "john@gmail.com",
|
||||
InvestorType:"Accredited Investors",
|
||||
InvestorType: "Accredited Investors",
|
||||
bankName: "Lorem Text",
|
||||
branchAddress: "Hohenzollernring 58, 95444",
|
||||
iban: "DE 1234 5678 9012 3456",
|
||||
@@ -568,7 +568,7 @@ const GlobalStateProvider = ({ children }) => {
|
||||
phoneNumber: "8940035906",
|
||||
address: "Saudi Arabia",
|
||||
emailID: "john@gmail.com",
|
||||
InvestorType:"Retail",
|
||||
InvestorType: "Retail",
|
||||
bankName: "Lorem Text",
|
||||
branchAddress: "Hohenzollernring 58, 95444",
|
||||
iban: "DE 1234 5678 9012 3456",
|
||||
@@ -587,7 +587,7 @@ const GlobalStateProvider = ({ children }) => {
|
||||
phoneNumber: "8940035906",
|
||||
address: "Saudi Arabia",
|
||||
emailID: "john@gmail.com",
|
||||
InvestorType:"Accredited Investors",
|
||||
InvestorType: "Accredited Investors",
|
||||
bankName: "Lorem Text",
|
||||
branchAddress: "Hohenzollernring 58, 95444",
|
||||
iban: "DE 1234 5678 9012 3456",
|
||||
@@ -606,7 +606,7 @@ const GlobalStateProvider = ({ children }) => {
|
||||
phoneNumber: "8940035906",
|
||||
address: "Saudi Arabia",
|
||||
emailID: "john@gmail.com",
|
||||
InvestorType:"Retail",
|
||||
InvestorType: "Retail",
|
||||
bankName: "Lorem Text",
|
||||
branchAddress: "Hohenzollernring 58, 95444",
|
||||
iban: "DE 1234 5678 9012 3456",
|
||||
@@ -625,7 +625,7 @@ const GlobalStateProvider = ({ children }) => {
|
||||
phoneNumber: "8940035906",
|
||||
address: "Saudi Arabia",
|
||||
emailID: "john@gmail.com",
|
||||
InvestorType:"Accredited Investors",
|
||||
InvestorType: "Accredited Investors",
|
||||
bankName: "Lorem Text",
|
||||
branchAddress: "Hohenzollernring 58, 95444",
|
||||
iban: "DE 1234 5678 9012 3456",
|
||||
@@ -644,7 +644,7 @@ const GlobalStateProvider = ({ children }) => {
|
||||
phoneNumber: "8940035906",
|
||||
address: "Saudi Arabia",
|
||||
emailID: "john@gmail.com",
|
||||
InvestorType:"Retail",
|
||||
InvestorType: "Retail",
|
||||
bankName: "Lorem Text",
|
||||
branchAddress: "Hohenzollernring 58, 95444",
|
||||
iban: "DE 1234 5678 9012 3456",
|
||||
@@ -663,7 +663,7 @@ const GlobalStateProvider = ({ children }) => {
|
||||
phoneNumber: "8940035906",
|
||||
address: "Saudi Arabia",
|
||||
emailID: "john@gmail.com",
|
||||
InvestorType:"Accredited Investors",
|
||||
InvestorType: "Accredited Investors",
|
||||
bankName: "Lorem Text",
|
||||
branchAddress: "Hohenzollernring 58, 95444",
|
||||
iban: "DE 1234 5678 9012 3456",
|
||||
@@ -682,7 +682,7 @@ const GlobalStateProvider = ({ children }) => {
|
||||
phoneNumber: "8940035906",
|
||||
address: "Saudi Arabia",
|
||||
emailID: "john@gmail.com",
|
||||
InvestorType:"Retail",
|
||||
InvestorType: "Retail",
|
||||
bankName: "Lorem Text",
|
||||
branchAddress: "Hohenzollernring 58, 95444",
|
||||
iban: "DE 1234 5678 9012 3456",
|
||||
@@ -701,7 +701,7 @@ const GlobalStateProvider = ({ children }) => {
|
||||
phoneNumber: "8940035906",
|
||||
address: "Saudi Arabia",
|
||||
emailID: "john@gmail.com",
|
||||
InvestorType:"Accredited Investors",
|
||||
InvestorType: "Accredited Investors",
|
||||
bankName: "Lorem Text",
|
||||
branchAddress: "Hohenzollernring 58, 95444",
|
||||
iban: "DE 1234 5678 9012 3456",
|
||||
@@ -712,136 +712,8 @@ const GlobalStateProvider = ({ children }) => {
|
||||
},
|
||||
]);
|
||||
|
||||
const [viewInvestor, setViewInvestor] = useState([
|
||||
{
|
||||
id: 1,
|
||||
dealId: "UWE3424992",
|
||||
dealName: "KKR Private Equity Fund",
|
||||
sponsorName: "KKR",
|
||||
investAmount: "$100,000",
|
||||
holdingPeriod: "4-5 years",
|
||||
estimatedReturn: "50-60%",
|
||||
kycStatus: "Open",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
dealId: "UWE3424992",
|
||||
dealName: "Blackstone Real Estate Income Trust",
|
||||
sponsorName: "Blackstone",
|
||||
investAmount: "$100,000",
|
||||
holdingPeriod: "4-5 years",
|
||||
estimatedReturn: "50-60%",
|
||||
kycStatus: "Pending",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
dealId: "UWE3424992",
|
||||
dealName: "J.P. Morgan",
|
||||
sponsorName: "J.P. Morgan",
|
||||
investAmount: "$100,000",
|
||||
holdingPeriod: "4-5 years",
|
||||
estimatedReturn: "50-60%",
|
||||
kycStatus: "Closed",
|
||||
},
|
||||
]);
|
||||
const [transaction, setTransaction] = useState([
|
||||
{
|
||||
id: 1,
|
||||
date: "2-Jan-24",
|
||||
transaction: "Deposit",
|
||||
currency: "BHD",
|
||||
amount: "12000.00",
|
||||
fromUSD: "",
|
||||
toUSD: "2.6376",
|
||||
USDamount: "31,651.20",
|
||||
IOName:"",
|
||||
paymentMethod:"Bank"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
date: "6-Jan-24",
|
||||
transaction: "Invested",
|
||||
currency: "BHD",
|
||||
amount: "-4000.00",
|
||||
fromUSD: "",
|
||||
toUSD: "2.6376",
|
||||
USDamount: "-10,550.40",
|
||||
IOName:"KKR Private Equity Fund",
|
||||
paymentMethod:"-"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
date: "7-Jan-24",
|
||||
transaction: "Deposit",
|
||||
currency: "BHD",
|
||||
amount: "4000.00",
|
||||
fromUSD: "",
|
||||
toUSD: "2.6376",
|
||||
USDamount: "10,550.40",
|
||||
IOName:"-",
|
||||
paymentMethod:"Apple Pay"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
date: "8-Jan-24",
|
||||
transaction: "Invested",
|
||||
currency: "BHD",
|
||||
amount: "-3000.00",
|
||||
fromUSD: "",
|
||||
toUSD: "2.6376",
|
||||
USDamount: "-7,912.80",
|
||||
IOName:"Black Stone Real Estate Income Trust",
|
||||
paymentMethod:"-"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
date: "2-Jan-24",
|
||||
transaction: "Deposit",
|
||||
currency: "BHD",
|
||||
amount: "12000.00",
|
||||
fromUSD: "0.3747",
|
||||
toUSD: "",
|
||||
USDamount: "31,651.20",
|
||||
IOName:"KKR Private Equity Fund",
|
||||
paymentMethod:"-"
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
date: "2-Jan-24",
|
||||
transaction: "Deposit",
|
||||
currency: "BHD",
|
||||
amount: "12000.00",
|
||||
fromUSD: "0.3747",
|
||||
toUSD: "",
|
||||
USDamount: "31,651.20",
|
||||
IOName:"Black Stone Real Estate Income Trust",
|
||||
paymentMethod:"-"
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
date: "2-Jan-24",
|
||||
transaction: "Deposit",
|
||||
currency: "BHD",
|
||||
amount: "12000.00",
|
||||
fromUSD: "0.3747",
|
||||
toUSD: "",
|
||||
USDamount: "31,651.20",
|
||||
IOName:"KKR Private Equity Fund",
|
||||
paymentMethod:"-"
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
date: "2-Jan-24",
|
||||
transaction: "Deposit",
|
||||
currency: "BHD",
|
||||
amount: "12000.00",
|
||||
fromUSD: "",
|
||||
toUSD: "2.6376",
|
||||
USDamount: "31,651.20",
|
||||
IOName:"",
|
||||
paymentMethod:"Bank"
|
||||
},
|
||||
]);
|
||||
const [viewInvestor, setViewInvestor] = useState([]);
|
||||
const [transaction, setTransaction] = useState([]);
|
||||
|
||||
const [investorTransaction, setInvestorTransaction] = useState([
|
||||
{
|
||||
@@ -1090,112 +962,14 @@ const GlobalStateProvider = ({ children }) => {
|
||||
]);
|
||||
const [deleteHistory, setDeleteHistory] = useState([
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: "2024-01-15",
|
||||
Distribution: "Office supplies",
|
||||
charge: "200.50",
|
||||
year: "2024",
|
||||
quarter: "Q1",
|
||||
amount: 1500,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: "2024-02-10",
|
||||
Distribution: "Marketing expenses",
|
||||
charge: "450.00",
|
||||
year: "2024",
|
||||
quarter: "Q1",
|
||||
amount: 2000,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: "2024-03-05",
|
||||
Distribution: "Software subscription",
|
||||
charge: "300.75",
|
||||
year: "2024",
|
||||
quarter: "Q1",
|
||||
amount: 1200,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: "2024-04-18",
|
||||
Distribution: "Travel expenses",
|
||||
charge: "600.30",
|
||||
year: "2024",
|
||||
quarter: "Q2",
|
||||
amount: 2500,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: "2024-05-22",
|
||||
Distribution: "Consulting fees",
|
||||
charge: "800.00",
|
||||
year: "2024",
|
||||
quarter: "Q2",
|
||||
amount: 3000,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: "2024-06-14",
|
||||
Distribution: "Office rent",
|
||||
charge: "1200.25",
|
||||
year: "2024",
|
||||
quarter: "Q2",
|
||||
amount: 3500,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: "2024-07-09",
|
||||
Distribution: "Utilities",
|
||||
charge: "150.40",
|
||||
year: "2024",
|
||||
quarter: "Q3",
|
||||
amount: 1000,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: "2024-08-29",
|
||||
Distribution: "Employee training",
|
||||
charge: "500.00",
|
||||
year: "2024",
|
||||
quarter: "Q3",
|
||||
amount: 1800,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: "2024-09-13",
|
||||
Distribution: "Website maintenance",
|
||||
charge: "350.65",
|
||||
year: "2024",
|
||||
quarter: "Q3",
|
||||
amount: 1400,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: "2024-10-23",
|
||||
Distribution: "Advertising",
|
||||
charge: "900.50",
|
||||
year: "2024",
|
||||
quarter: "Q4",
|
||||
amount: 2200,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: "2024-10-23",
|
||||
Distribution: "Advertising",
|
||||
charge: "900.50",
|
||||
year: "2024",
|
||||
quarter: "Q4",
|
||||
amount: 2200,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: "2024-10-23",
|
||||
Distribution: "Advertising",
|
||||
charge: "900.50",
|
||||
year: "2024",
|
||||
quarter: "Q4",
|
||||
amount: 2200,
|
||||
id: 1,
|
||||
firstName: "satyam",
|
||||
lastName: "Bendal",
|
||||
clientId: "QA00000003",
|
||||
RequestedOn: "2024-08-21T08:12:08.000Z",
|
||||
phoneNumber: "6387524874",
|
||||
country: "Qatar",
|
||||
status: "Approved",
|
||||
},
|
||||
]);
|
||||
const [investorRequest, setInvestorRequest] = useState([
|
||||
@@ -1310,114 +1084,26 @@ const GlobalStateProvider = ({ children }) => {
|
||||
]);
|
||||
const [deleteRequest, setDeleteRequest] = useState([
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: getRandomDate(startDate, endDate),
|
||||
Distribution: "lorem ipsum dummy text",
|
||||
charge: "500",
|
||||
year: "2024",
|
||||
quater: "Q 1",
|
||||
amount: 1000,
|
||||
"id": 2,
|
||||
"firstName": "satyam",
|
||||
"lastName": "Bendal",
|
||||
"clientId": "QA00000003",
|
||||
"RequestedOn": "2024-08-21T09:44:21.000Z",
|
||||
"phoneNumber": "6387524874",
|
||||
"country": "Qatar",
|
||||
"status": "Pending"
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: getRandomDate(startDate, endDate),
|
||||
Distribution: "lorem ipsum dummy text",
|
||||
charge: "500",
|
||||
year: "2024",
|
||||
quater: "Q 1",
|
||||
amount: 1000,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: getRandomDate(startDate, endDate),
|
||||
Distribution: "lorem ipsum dummy text",
|
||||
charge: "500",
|
||||
year: "2024",
|
||||
quater: "Q 1",
|
||||
amount: 1000,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: getRandomDate(startDate, endDate),
|
||||
Distribution: "lorem ipsum dummy text",
|
||||
charge: "500",
|
||||
year: "2024",
|
||||
quater: "Q 1",
|
||||
amount: 1000,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: getRandomDate(startDate, endDate),
|
||||
Distribution: "lorem ipsum dummy text",
|
||||
charge: "500",
|
||||
year: "2024",
|
||||
quater: "Q 1",
|
||||
amount: 1000,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: getRandomDate(startDate, endDate),
|
||||
Distribution: "lorem ipsum dummy text",
|
||||
charge: "500",
|
||||
year: "2024",
|
||||
quater: "Q 1",
|
||||
amount: 1000,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: getRandomDate(startDate, endDate),
|
||||
Distribution: "lorem ipsum dummy text",
|
||||
charge: "500",
|
||||
year: "2024",
|
||||
quater: "Q 1",
|
||||
amount: 1000,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: getRandomDate(startDate, endDate),
|
||||
Distribution: "lorem ipsum dummy text",
|
||||
charge: "500",
|
||||
year: "2024",
|
||||
quater: "Q 1",
|
||||
amount: 1000,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: getRandomDate(startDate, endDate),
|
||||
Distribution: "lorem ipsum dummy text",
|
||||
charge: "500",
|
||||
year: "2024",
|
||||
quater: "Q 1",
|
||||
amount: 1000,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: getRandomDate(startDate, endDate),
|
||||
Distribution: "lorem ipsum dummy text",
|
||||
charge: "500",
|
||||
year: "2024",
|
||||
quater: "Q 1",
|
||||
amount: 1000,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: getRandomDate(startDate, endDate),
|
||||
Distribution: "lorem ipsum dummy text",
|
||||
charge: "500",
|
||||
year: "2024",
|
||||
quater: "Q 1",
|
||||
amount: 1000,
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
date: getRandomDate(startDate, endDate),
|
||||
Distribution: "lorem ipsum dummy text",
|
||||
charge: "500",
|
||||
year: "2024",
|
||||
quater: "Q 1",
|
||||
amount: 1000,
|
||||
},
|
||||
]);
|
||||
"id": 3,
|
||||
"firstName": "satyam",
|
||||
"lastName": "Bendal",
|
||||
"clientId": "QA00000003",
|
||||
"RequestedOn": "2024-08-21T09:53:03.000Z",
|
||||
"phoneNumber": "6387524874",
|
||||
"country": "Qatar",
|
||||
"status": "Pending"
|
||||
}
|
||||
]);
|
||||
const [viewIO, setViewIO] = useState([
|
||||
{
|
||||
id: 1,
|
||||
@@ -1449,7 +1135,6 @@ const GlobalStateProvider = ({ children }) => {
|
||||
},
|
||||
]);
|
||||
|
||||
|
||||
const [depositRequest, setDepositRequest] = useState([
|
||||
{
|
||||
id: 1,
|
||||
@@ -1683,6 +1368,28 @@ const GlobalStateProvider = ({ children }) => {
|
||||
mailId: "john@gmail.com",
|
||||
status: "Incompleted",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
date: "02-Jan-2024",
|
||||
clientId: "SA00000001",
|
||||
firstName: "John",
|
||||
lastName: "David",
|
||||
country: "Saudi Arabia",
|
||||
phoneNumber: "8940035906",
|
||||
mailId: "john@gmail.com",
|
||||
status: "Incompleted",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
date: "02-Jan-2024",
|
||||
clientId: "SA00000001",
|
||||
firstName: "John",
|
||||
lastName: "David",
|
||||
country: "Saudi Arabia",
|
||||
phoneNumber: "8940035906",
|
||||
mailId: "john@gmail.com",
|
||||
status: "Incompleted",
|
||||
},
|
||||
]);
|
||||
|
||||
const [manageAcademy, setManageAcademy] = useState([
|
||||
@@ -1723,15 +1430,328 @@ const GlobalStateProvider = ({ children }) => {
|
||||
]);
|
||||
|
||||
|
||||
const [users, setUsers] = useState([
|
||||
{
|
||||
id: 1,
|
||||
firstName: "SA00000001",
|
||||
lastName: "John David",
|
||||
emailID: "John",
|
||||
role: "David",
|
||||
phoneNumber:"8940035906",
|
||||
},
|
||||
]);
|
||||
|
||||
const [fawateerRequest, setFawateerRequest] = useState([
|
||||
{
|
||||
id: 8,
|
||||
principal_xid: 2,
|
||||
transaction_date: "2024-10-07",
|
||||
transaction_amount: "2000.00",
|
||||
transactionStatus: "Approved",
|
||||
makerComment: "This is a sample comment for the transactio",
|
||||
checkerComment: "This is a sample comment for the transaction",
|
||||
spportFile_path: "public\\spportFile_path\\screenshot_2024_02_15_152810.png",
|
||||
clientReference_id: "BH00000001",
|
||||
firstName: "jayesh",
|
||||
lastName: "jain ",
|
||||
mobileNumber: "+919819906537",
|
||||
emailAddress: "jayeshkjain6@gmail.com",
|
||||
maker: {
|
||||
firstName: "Faisal",
|
||||
lastName: "Aljalahma",
|
||||
emailAddress: "admin@tanami.com",
|
||||
ISDcode: "+973",
|
||||
mobileNumber: "3633133"
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
principal_xid: 2,
|
||||
transaction_date: "2024-10-07",
|
||||
transaction_amount: "2000.00",
|
||||
transactionStatus: "Approved",
|
||||
makerComment: "This is a sample comment for the transactio",
|
||||
checkerComment: "This is a sample comment for the transaction",
|
||||
spportFile_path: "public\\spportFile_path\\screenshot_2024_02_15_152810.png",
|
||||
clientReference_id: "BH00000001",
|
||||
firstName: "jayesh",
|
||||
lastName: "jain ",
|
||||
mobileNumber: "+919819906537",
|
||||
emailAddress: "jayeshkjain6@gmail.com",
|
||||
maker: {
|
||||
firstName: "Faisal",
|
||||
lastName: "Aljalahma",
|
||||
emailAddress: "admin@tanami.com",
|
||||
ISDcode: "+973",
|
||||
mobileNumber: "3633133"
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
principal_xid: 2,
|
||||
transaction_date: "2024-10-07",
|
||||
transaction_amount: "2000.00",
|
||||
transactionStatus: "Approved",
|
||||
makerComment: "This is a sample comment for the transactio",
|
||||
checkerComment: "This is a sample comment for the transaction",
|
||||
spportFile_path: "public\\spportFile_path\\screenshot_2024_02_15_152810.png",
|
||||
clientReference_id: "BH00000001",
|
||||
firstName: "jayesh",
|
||||
lastName: "jain ",
|
||||
mobileNumber: "+919819906537",
|
||||
emailAddress: "jayeshkjain6@gmail.com",
|
||||
maker: {
|
||||
firstName: "Faisal",
|
||||
lastName: "Aljalahma",
|
||||
emailAddress: "admin@tanami.com",
|
||||
ISDcode: "+973",
|
||||
mobileNumber: "3633133"
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
principal_xid: 2,
|
||||
transaction_date: "2024-10-07",
|
||||
transaction_amount: "2000.00",
|
||||
transactionStatus: "Approved",
|
||||
makerComment: "This is a sample comment for the transactio",
|
||||
checkerComment: "This is a sample comment for the transaction",
|
||||
spportFile_path: "public\\spportFile_path\\screenshot_2024_02_15_152810.png",
|
||||
clientReference_id: "BH00000001",
|
||||
firstName: "jayesh",
|
||||
lastName: "jain ",
|
||||
mobileNumber: "+919819906537",
|
||||
emailAddress: "jayeshkjain6@gmail.com",
|
||||
maker: {
|
||||
firstName: "Faisal",
|
||||
lastName: "Aljalahma",
|
||||
emailAddress: "admin@tanami.com",
|
||||
ISDcode: "+973",
|
||||
mobileNumber: "3633133"
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
const [approveHistory, setApproveHistory] = useState([
|
||||
{
|
||||
"id": 1,
|
||||
"principal_xid": 2,
|
||||
"transaction_date": "2024-10-07",
|
||||
"transaction_amount": "2000.00",
|
||||
"transactionStatus": "Pending",
|
||||
"makerComment": null,
|
||||
"checkerComment": null,
|
||||
"spportFile_path": null,
|
||||
"clientReference_id": "BH00000001",
|
||||
"firstName": "jayesh",
|
||||
"lastName": "jain",
|
||||
"mobileNumber": "+919819906537",
|
||||
"emailAddress": "jayeshkjain6@gmail.com",
|
||||
"maker": {
|
||||
"firstName": "Faisal",
|
||||
"lastName": "Aljalahma",
|
||||
"emailAddress": "admin@tanami.com",
|
||||
"ISDcode": "+973",
|
||||
"mobileNumber": "3633133"
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
const [approved, setApproved] = useState([
|
||||
{
|
||||
id: 1,
|
||||
transactionDate: "02-Jan-24",
|
||||
particulars: "Cash Reserve- Initated",
|
||||
amount: "50,000.00",
|
||||
Comments: "",
|
||||
user: "Faisal",
|
||||
entryDate: "02-Jan-24",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
transactionDate: "12-Feb-24",
|
||||
particulars: "Fees & Expense",
|
||||
amount: "-22,000.00",
|
||||
Comments: "",
|
||||
user: "Faisal",
|
||||
entryDate: "13-Feb-24",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
transactionDate: "12-Feb-24",
|
||||
particulars: "Distribution Received From Sponsor",
|
||||
amount: "50,000.00",
|
||||
Comments: "",
|
||||
user: "Nawab",
|
||||
entryDate: "24-Mar-24",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
transactionDate: "28-Mar-24",
|
||||
particulars: "Distribution Paid To Investors",
|
||||
amount: "-40,000.00",
|
||||
Comments: "",
|
||||
user: "Faisal",
|
||||
entryDate: "28-Mar-24",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
transactionDate: "26-Jun-24",
|
||||
particulars: "Distribution Received From Sponsor",
|
||||
amount: "70,000.00",
|
||||
Comments: "",
|
||||
user: "Faisal",
|
||||
entryDate: "27-Jun-24",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
transactionDate: "28-Jun-24",
|
||||
particulars: "Distribution Paid To Investors",
|
||||
amount: "-60,000.00",
|
||||
Comments: "",
|
||||
user: "Nawab",
|
||||
entryDate: "28-Jun-24",
|
||||
},
|
||||
]);
|
||||
|
||||
const [iONAVDetail, setIONAVDetail] = useState([
|
||||
{
|
||||
id: 1,
|
||||
valuationDate: "01-Jul-24",
|
||||
nav: "1,229,750.00 ",
|
||||
lastUpdate: "12.56",
|
||||
investmentClose: "29.45",
|
||||
updatedBy: "Nawab",
|
||||
updatedOn: "01-Jul-24",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
valuationDate: "25-Apr-24",
|
||||
nav: "1,092,500.00",
|
||||
lastUpdate: "15.00",
|
||||
investmentClose: "15.00",
|
||||
updatedBy: "Faisal",
|
||||
updatedOn: "25-Apr-24",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
valuationDate: "02-Jan-24",
|
||||
nav: "950,000.00",
|
||||
lastUpdate: "",
|
||||
investmentClose: "",
|
||||
updatedBy: "Faisal",
|
||||
updatedOn: "02-Jan-24",
|
||||
},
|
||||
]);
|
||||
|
||||
const [iOTransaction, setIOTransaction] = useState([
|
||||
{
|
||||
id: 1,
|
||||
transactionName: "Amount Invested",
|
||||
amount: "995,000",
|
||||
createdBy: "Faisal",
|
||||
createdOn: "27-Oct-24",
|
||||
approvedBy: "Nawab",
|
||||
approvedOn: "28-Oct-24",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
transactionName: "Distribution To Sponser",
|
||||
amount: "40,000",
|
||||
createdBy: "Faisal",
|
||||
createdOn: "30-Oct-24",
|
||||
approvedBy: "Nawab",
|
||||
approvedOn: "31-Oct-24",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
transactionName: "Amount Invested",
|
||||
amount: "995,000",
|
||||
createdBy: "Faisal",
|
||||
createdOn: "27-Oct-24",
|
||||
approvedBy: "Nawab",
|
||||
approvedOn: "28-Oct-24",
|
||||
},
|
||||
]);
|
||||
|
||||
const [opportunities, setOpportunities] = useState([
|
||||
{
|
||||
id: 1,
|
||||
Investors: "Blackstone",
|
||||
amountRemaining: "10 %",
|
||||
totalDealsize: "$ 520",
|
||||
noViews: "50",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
Investors: "Blackstone",
|
||||
amountRemaining: "10 %",
|
||||
totalDealsize: "$ 520",
|
||||
noViews: "50",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
Investors: "Blackstone",
|
||||
amountRemaining: "10 %",
|
||||
totalDealsize: "$ 520",
|
||||
noViews: "50",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
Investors: "Blackstone",
|
||||
amountRemaining: "10 %",
|
||||
totalDealsize: "$ 520",
|
||||
noViews: "50",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
Investors: "Blackstone",
|
||||
amountRemaining: "10 %",
|
||||
totalDealsize: "$ 520",
|
||||
noViews: "50",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
Investors: "Blackstone",
|
||||
amountRemaining: "10 %",
|
||||
totalDealsize: "$ 520",
|
||||
noViews: "50",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
Investors: "Blackstone",
|
||||
amountRemaining: "10 %",
|
||||
totalDealsize: "$ 520",
|
||||
noViews: "50",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
Investors: "Blackstone",
|
||||
amountRemaining: "10 %",
|
||||
totalDealsize: "$ 520",
|
||||
noViews: "50",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
Investors: "Blackstone",
|
||||
amountRemaining: "10 %",
|
||||
totalDealsize: "$ 520",
|
||||
noViews: "50",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
Investors: "Blackstone",
|
||||
amountRemaining: "10 %",
|
||||
totalDealsize: "$ 520",
|
||||
noViews: "50",
|
||||
},
|
||||
]);
|
||||
|
||||
const [InvestorWallet, setInvestorWallet] = useState(null);
|
||||
|
||||
// ==============[ prod state ]===============================
|
||||
const [IODetails, setIODetails] = useState(null);
|
||||
const [ isIOloading, setIOloading ] = useState(false)
|
||||
const [isIOloading, setIOloading] = useState(false);
|
||||
|
||||
return (
|
||||
<GlobalStateContext.Provider
|
||||
@@ -1804,10 +1824,24 @@ const GlobalStateProvider = ({ children }) => {
|
||||
setAcademicDocuments,
|
||||
iOArtifactsTwo,
|
||||
setIOArtifactsTwo,
|
||||
|
||||
|
||||
isIOloading,
|
||||
setIOloading
|
||||
InvestorWallet,
|
||||
setInvestorWallet,
|
||||
isIOloading,
|
||||
setIOloading,
|
||||
users,
|
||||
setUsers,
|
||||
fawateerRequest,
|
||||
setFawateerRequest,
|
||||
approveHistory,
|
||||
setApproveHistory,
|
||||
approved,
|
||||
setApproved,
|
||||
iONAVDetail,
|
||||
setIONAVDetail,
|
||||
iOTransaction,
|
||||
setIOTransaction,
|
||||
opportunities,
|
||||
setOpportunities,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
4
src/Images/dash_icon_1.svg
Normal 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 |
4
src/Images/dash_icon_2.svg
Normal 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 |
7
src/Images/dash_icon_3.svg
Normal 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
|
After Width: | Height: | Size: 2.1 KiB |
3
src/Images/dash_icon_4.svg
Normal 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 |
3
src/Images/dash_icon_5.svg
Normal 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 |
5
src/Images/dash_icon_6.svg
Normal 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
|
After Width: | Height: | Size: 3.5 KiB |
3
src/Images/dash_icon_7.svg
Normal 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 |
@@ -7,70 +7,103 @@ import {
|
||||
Input,
|
||||
Text,
|
||||
Tooltip,
|
||||
useBoolean,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
||||
import DataTable from "../../Components/DataTable/DataTable";
|
||||
import NormalTable from "../../Components/DataTable/NormalTable";
|
||||
import Pagination from "../../Components/Pagination";
|
||||
import GlobalStateContext from "../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../Components/CustomAlertDialog";
|
||||
import { formatDate } from "../../Components/Functions/UTCConvertor";
|
||||
import { CheckIcon, CloseIcon } from "@chakra-ui/icons";
|
||||
import { useGetDeleteHistoryQuery } from "../../Services/delete.request.service";
|
||||
import { TABLE_PAGINATION } from "../../Constants/Paginations";
|
||||
import InitiateReversalPopup from "../../Components/Popups/InitiateReversalPopups";
|
||||
import ToastBox from "../../Components/ToastBox";
|
||||
import { useCreateAccountDeletionReversalRequestMutation } from "../../Services/reversal.account.deletion.service";
|
||||
import { isMaker } from "../../Constants/Constants";
|
||||
// import { formatDate } from "../../Components/Functions/UTCConvertor";
|
||||
|
||||
const DeletionHistory = () => {
|
||||
const toast = useToast();
|
||||
const { slideFromRight, deleteHistory, setDeleteHistory } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const { slideFromRight, setDeleteHistory } = useContext(GlobalStateContext);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
// =========================== [Use State] =============================
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
const [reversalId, setReversalId] = useState();
|
||||
const {
|
||||
isOpen: isOpenInRev,
|
||||
onOpen: onOpenInRev,
|
||||
onClose: onCloseInRev,
|
||||
} = useDisclosure();
|
||||
const [isReversalLoading, setIsReversalLoading] = useBoolean();
|
||||
|
||||
// ====================================================[Table Filter]================================================================
|
||||
const filteredData = deleteHistory.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item.Distribution;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
const [createAccountDeletionReversalRequest] =
|
||||
useCreateAccountDeletionReversalRequestMutation();
|
||||
|
||||
// Filter by status
|
||||
// const status = item.status;
|
||||
// const statusLower = status ? "active" : "inactive";
|
||||
// Debounce the search term to avoid making a request on every keystroke
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedSearchTerm(searchTerm);
|
||||
}, 500); // Adjust delay as needed
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [searchTerm]);
|
||||
|
||||
// const statusMatches =
|
||||
// statusFilter === "all" ||
|
||||
// (statusFilter === "active" && status === true) ||
|
||||
// (statusFilter === "inactive" && status === false);
|
||||
const {
|
||||
data: deleteHistory,
|
||||
isLoading,
|
||||
refetch,
|
||||
} = useGetDeleteHistoryQuery(
|
||||
{
|
||||
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
|
||||
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
|
||||
search: debouncedSearchTerm,
|
||||
},
|
||||
{
|
||||
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
|
||||
}
|
||||
);
|
||||
|
||||
return nameMatches;
|
||||
});
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
// Use useEffect to refetch data when the component mounts
|
||||
useEffect(() => {
|
||||
refetch();
|
||||
}, [refetch]);
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr No.",
|
||||
"Date",
|
||||
"Distribution Amount",
|
||||
"Charges (USD)",
|
||||
"Year",
|
||||
"Quater",
|
||||
"Action",
|
||||
"Request On",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Country",
|
||||
"Phone Number",
|
||||
"Status",
|
||||
isMaker() && "Reversal Action",
|
||||
];
|
||||
|
||||
const extractedArray = filteredData?.map((item, index) => ({
|
||||
const extractedArray = deleteHistory?.data?.rows?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr No.": (
|
||||
<Text
|
||||
@@ -78,83 +111,122 @@ const DeletionHistory = () => {
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{index + 1}.
|
||||
</Text>
|
||||
),
|
||||
"Date": (
|
||||
"Request On": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{formatDate(item.date)}
|
||||
{formatDate(item.Requested_on)}
|
||||
</Text>
|
||||
),
|
||||
"Distribution Amount": (
|
||||
"Client ID": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{item.Distribution}
|
||||
{item.clientId}
|
||||
</Text>
|
||||
),
|
||||
"Charges (USD)": (
|
||||
"First Name": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{item.charge}
|
||||
{item.firstName}
|
||||
{/* {formatDate(item.charge)} */}
|
||||
</Text>
|
||||
),
|
||||
Year: (
|
||||
"Last Name": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{item.year}
|
||||
{item.lastName}
|
||||
</Text>
|
||||
),
|
||||
Quater: (
|
||||
<Text
|
||||
Country: (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{item.quarter}
|
||||
{item.country}
|
||||
</Text>
|
||||
),
|
||||
Action: (
|
||||
<Box display={'flex'} justifyContent={'space-around'}>
|
||||
<Tooltip rounded={'sm'} fontSize={'xs'} label='Accept' bg='#fff' color={'green.500'} placement="left-start">
|
||||
<Button color="green.500" rounded={'sm'} size={'xs'}>
|
||||
<CheckIcon /></Button></Tooltip>
|
||||
<Tooltip rounded={'sm'} fontSize={'xs'} label='Reject' bg='#fff' color={'red.500'} placement="left-start">
|
||||
<Button color="red.500" rounded={'sm'} size={'xs'}>
|
||||
<CloseIcon /></Button></Tooltip>
|
||||
"Phone Number": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{item.phoneNumber}
|
||||
</Text>
|
||||
),
|
||||
Status: (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
as={"span"}
|
||||
color={item?.deletionStatus === "Approved" ? "red.500" : "blue.500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={"600"}
|
||||
>
|
||||
{item.deletionStatus}
|
||||
</Text>
|
||||
),
|
||||
"Reversal Action": (
|
||||
<Box w={"120px"} isTruncated={true} cursor={"pointer"}>
|
||||
{item.deletionStatus === "Approved" ? (
|
||||
<Text
|
||||
as={"span"}
|
||||
color={!item.isReversal ? "green.500" : "#FFBB00"}
|
||||
fontWeight={700}
|
||||
>
|
||||
{!item.isReversal ? (
|
||||
<Button
|
||||
onClick={() => {
|
||||
onOpenInRev(); // Call the function
|
||||
setReversalId(item.id);
|
||||
}}
|
||||
colorScheme="teal"
|
||||
size="xs"
|
||||
variant="outline"
|
||||
>
|
||||
Initiate Reversal
|
||||
</Button>
|
||||
) : (
|
||||
"Under process"
|
||||
)}
|
||||
</Text>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const deleteHistory = sponser.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
const deleteHistory = sponser.filter((sponsor) => sponsor.id !== actionId);
|
||||
|
||||
setTimeout(() => {
|
||||
setSponser(deleteHistory);
|
||||
@@ -164,6 +236,45 @@ const DeletionHistory = () => {
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
const handleApproved = async (data) => {
|
||||
setIsReversalLoading.on(); // Start loading
|
||||
try {
|
||||
const { error, data: responseData } =
|
||||
await createAccountDeletionReversalRequest({
|
||||
id: reversalId,
|
||||
data,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error; // Explicitly handle the error
|
||||
}
|
||||
|
||||
// Success: Perform necessary actions
|
||||
refetch();
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={responseData?.message || "Action successful!"} />
|
||||
),
|
||||
});
|
||||
onCloseInRev();
|
||||
} catch (error) {
|
||||
// Handle errors
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox
|
||||
message={
|
||||
error?.data?.message || "Something went wrong. Please try again."
|
||||
}
|
||||
status="error"
|
||||
/>
|
||||
),
|
||||
});
|
||||
console.error("Error:", error);
|
||||
} finally {
|
||||
setIsReversalLoading.off(); // Ensure loading is toggled off
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
|
||||
<Box bg="white.500">
|
||||
@@ -187,13 +298,13 @@ const DeletionHistory = () => {
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<HStack display={"flex"} alignItems={"center"}>
|
||||
{/* <HStack display={"flex"} alignItems={"center"}>
|
||||
<Pagination totalItems={10} />
|
||||
</HStack>
|
||||
</HStack> */}
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<DataTable
|
||||
<NormalTable
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
@@ -213,6 +324,12 @@ const DeletionHistory = () => {
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<InitiateReversalPopup
|
||||
onClose={onCloseInRev}
|
||||
isOpen={isOpenInRev}
|
||||
handelApproved={handleApproved}
|
||||
isLoading={isReversalLoading}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,15 +6,20 @@ import {
|
||||
HStack,
|
||||
Input,
|
||||
Text,
|
||||
Tooltip,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
||||
import DataTable from "../../Components/DataTable/DataTable";
|
||||
import NormalTable from "../../Components/DataTable/NormalTable";
|
||||
import Pagination from "../../Components/Pagination";
|
||||
import GlobalStateContext from "../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../Components/CustomAlertDialog";
|
||||
import { formatDate } from "../../Components/Functions/UTCConvertor";
|
||||
import { CheckIcon, CloseIcon } from "@chakra-ui/icons";
|
||||
import DeletionRequestApprove from "./DeletionRequestApprove";
|
||||
import { useGetDeleteRequestQuery } from "../../Services/delete.request.service";
|
||||
// import { formatDate } from "../../Components/Functions/UTCConvertor";
|
||||
|
||||
const DeletionRequest = () => {
|
||||
@@ -28,6 +33,32 @@ const DeletionRequest = () => {
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
const {
|
||||
data,
|
||||
isLoading: DeletionLoading
|
||||
} = useGetDeleteRequestQuery()
|
||||
|
||||
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
onOpen: onConfirmOpen,
|
||||
onClose: onConfirmClose,
|
||||
} = useDisclosure();
|
||||
|
||||
const {
|
||||
isOpen: isRejectOpen,
|
||||
onOpen: onRejectOpen,
|
||||
onClose: onRejectClose,
|
||||
} = useDisclosure();
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
@@ -39,9 +70,9 @@ const DeletionRequest = () => {
|
||||
}, []);
|
||||
|
||||
// ====================================================[Table Filter]================================================================
|
||||
const filteredData = deleteRequest.filter((item) => {
|
||||
const filteredData = data?.data?.rows?.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item.Distribution;
|
||||
const name = item?.firstName;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
|
||||
@@ -60,19 +91,21 @@ const DeletionRequest = () => {
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr No.",
|
||||
"Date",
|
||||
"Distribution Amount",
|
||||
"Charges (USD)",
|
||||
"Year",
|
||||
"Quater",
|
||||
"Amount",
|
||||
"Requested On",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Country",
|
||||
"Phone Number",
|
||||
"Status",
|
||||
"Action"
|
||||
];
|
||||
|
||||
const extractedArray = filteredData?.map((item, index) => ({
|
||||
const extractedArray = data?.data?.rows?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr No.": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
@@ -81,74 +114,112 @@ const DeletionRequest = () => {
|
||||
{index + 1}.
|
||||
</Text>
|
||||
),
|
||||
"Date": (
|
||||
"Requested On": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{formatDate(item.date)}
|
||||
{formatDate(item?.Requested_on)}
|
||||
</Text>
|
||||
),
|
||||
"Distribution Amount": (
|
||||
"Client ID": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{item.Distribution}
|
||||
{item?.clientId}
|
||||
</Text>
|
||||
),
|
||||
"Charges (USD)": (
|
||||
"First Name": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{item.charge}
|
||||
{item?.firstName}
|
||||
{/* {formatDate(item.charge)} */}
|
||||
</Text>
|
||||
),
|
||||
Year: (
|
||||
"Last Name": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{item.year}
|
||||
{item?.lastName}
|
||||
</Text>
|
||||
),
|
||||
Quater: (
|
||||
Country: (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{item.quater}
|
||||
{item?.country}
|
||||
</Text>
|
||||
),
|
||||
|
||||
Amount: (
|
||||
"Phone Number": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{item.amount}
|
||||
{item?.phoneNumber}
|
||||
</Text>
|
||||
),
|
||||
Status:(<Box w={"auto"} display={'flex'} alignItems={'center'} isTruncated={true}>
|
||||
<Text
|
||||
as={'span'}
|
||||
fontWeight={"700"}
|
||||
textTransform={"none"}
|
||||
color={item.kycStatus ? "blue.500" : "red.500"}
|
||||
// px={2}
|
||||
py={0.5}
|
||||
variant={'solid'}
|
||||
|
||||
>
|
||||
{item.KYCStatus ? "Completed" : "Not complete"}
|
||||
</Text>
|
||||
</Box>),
|
||||
|
||||
Action: (
|
||||
<Box display={"flex"} justifyContent={"center"} gap={2}>
|
||||
<Button
|
||||
// colorScheme="forestGreen"
|
||||
// color="green.500"
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={2}
|
||||
py={1}
|
||||
onClick={() => {
|
||||
setActionId(item.id);
|
||||
onConfirmOpen();
|
||||
}}
|
||||
colorScheme="green"
|
||||
variant={"outline"}
|
||||
cursor={"pointer"}
|
||||
>
|
||||
Review
|
||||
</Button>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
@@ -187,17 +258,17 @@ const DeletionRequest = () => {
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<HStack display={"flex"} alignItems={"center"}>
|
||||
{/* <HStack display={"flex"} alignItems={"center"}>
|
||||
<Pagination totalItems={10} />
|
||||
</HStack>
|
||||
</HStack> */}
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<DataTable
|
||||
<NormalTable
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
isLoading={DeletionLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
// totalPages={10}
|
||||
@@ -213,6 +284,22 @@ const DeletionRequest = () => {
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
|
||||
|
||||
<DeletionRequestApprove
|
||||
data={deleteRequest}
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
id={actionId}
|
||||
// firstField={firstField}
|
||||
/>
|
||||
|
||||
|
||||
{/* <DepositRequestReject
|
||||
isOpen={isRejectOpen}
|
||||
onClose={onRejectClose}
|
||||
id={actionId}
|
||||
/> */}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
236
src/Pages/AccountDeletion/DeletionRequestApprove.jsx
Normal file
@@ -0,0 +1,236 @@
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import {
|
||||
useGetDepositRequestByIdQuery,
|
||||
useGetDepositRequestQuery,
|
||||
useUpdateDepositRequestMutation,
|
||||
} from "../../Services/deposit.request.service";
|
||||
import FullscreenLoaders from "../../Components/Loaders/FullscreenLoaders";
|
||||
import ToastBox from "../../Components/ToastBox";
|
||||
import { useGetDrawalRequestQuery } from "../../Services/drawal.request.service";
|
||||
import {
|
||||
useApproveDepositRequestMutation,
|
||||
useGetDeleteRequestByIdQuery,
|
||||
useGetDeleteRequestQuery,
|
||||
} from "../../Services/delete.request.service";
|
||||
|
||||
const FILE_TYPES = ["image/jpeg", "image/png", "image/gif"];
|
||||
|
||||
// export const conformModalSchema = yup.object().shape({
|
||||
// adminComment: yup.string().notRequired(),
|
||||
// });
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
adminComment: yup.string().notRequired(),
|
||||
});
|
||||
|
||||
const DeletionRequestApprove = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
firstField,
|
||||
id,
|
||||
data: requestData,
|
||||
}) => {
|
||||
const toast = useToast();
|
||||
const [file, setFile] = useState();
|
||||
const [isBtnLoading, setIsBtnLoading] = useState(false);
|
||||
const [isBtnLoadingReject, setIsBtnLoadingReject] = useState(false);
|
||||
const [isReject, setIsReject] = useState(false);
|
||||
|
||||
const fileredData = requestData?.find((item) => item?.id === id);
|
||||
const [updateApproveRequest] = useApproveDepositRequestMutation();
|
||||
const { data, isLoading } = useGetDeleteRequestByIdQuery(id, {
|
||||
skip: !id,
|
||||
});
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
mode: "all",
|
||||
});
|
||||
|
||||
const { refetch } = useGetDeleteRequestQuery();
|
||||
|
||||
useEffect(() => {
|
||||
reset({
|
||||
comment: fileredData?.comment,
|
||||
});
|
||||
}, [requestData, id]);
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
setIsBtnLoading(isReject ? false : true);
|
||||
setIsBtnLoadingReject(isReject);
|
||||
const approveReq = {
|
||||
adminComment: data?.adminComment,
|
||||
deletionStatus: isReject ? "Reject" : "Approved",
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await updateApproveRequest({ id, data: approveReq });
|
||||
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.error?.data?.message} status={"error"} />
|
||||
),
|
||||
});
|
||||
heandleOnClose();
|
||||
} else if (res?.data?.statusCode === 200) {
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.data?.message} />,
|
||||
});
|
||||
refetch();
|
||||
heandleOnClose();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const onReject = () => {};
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
reset({
|
||||
comment: data?.data?.comment,
|
||||
});
|
||||
}
|
||||
}, [data, reset]);
|
||||
|
||||
const heandleOnClose = () => {
|
||||
reset();
|
||||
onClose();
|
||||
setIsBtnLoading(false);
|
||||
setIsReject(false);
|
||||
setIsBtnLoadingReject(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onClose={heandleOnClose}
|
||||
initialFocusRef={firstField}
|
||||
>
|
||||
<ModalOverlay />
|
||||
|
||||
<ModalContent pb={4}>
|
||||
{/* <ModalHeader fontSize={"md"}>Confirm</ModalHeader> */}
|
||||
<ModalCloseButton />
|
||||
{isLoading ? (
|
||||
<FullscreenLoaders height={"50vh"} />
|
||||
) : (
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mt={6} mb={4}>
|
||||
<FormLabel fontSize="sm">
|
||||
Investor Comment{" "}
|
||||
<Badge colorScheme="green">{fileredData?.currencyCode}</Badge>
|
||||
</FormLabel>
|
||||
{/* <Textarea
|
||||
focusBorderColor="green.400"
|
||||
name="comment"
|
||||
{...register("comment")}
|
||||
fontSize="sm"
|
||||
type="text"
|
||||
size="sm"
|
||||
readOnly
|
||||
rows={5}
|
||||
/>
|
||||
{errors.comment && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.comment.message}
|
||||
</Text>
|
||||
)} */}
|
||||
|
||||
<Text fontSize="sm" fontWeight={500} color={"gray.600"}>
|
||||
{data?.data?.comment}
|
||||
</Text>
|
||||
</FormControl>
|
||||
<FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">Admin Comment</FormLabel>
|
||||
<Textarea
|
||||
rows={5}
|
||||
focusBorderColor="green.400"
|
||||
name="adminComment"
|
||||
{...register("adminComment")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="sm"
|
||||
placeholder={"Enter your comment...."}
|
||||
resize={"none"}
|
||||
mb={2}
|
||||
/>
|
||||
{errors.adminComment && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.adminComment.message}
|
||||
</Text>
|
||||
)}
|
||||
<FormHelperText fontSize="xs" color="gray.500">
|
||||
<Text as={"span"} me={2}>
|
||||
{" "}
|
||||
Maximum length should be 200 characters. You have entered
|
||||
</Text>
|
||||
{watch("adminComment")?.length || 0} characters.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
colorScheme="red"
|
||||
mr={3}
|
||||
type="submit"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
variant={"ghost"}
|
||||
onClick={() => setIsReject(true)}
|
||||
isLoading={isBtnLoadingReject}
|
||||
fontWeight={500}
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
>
|
||||
Approve
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeletionRequestApprove;
|
||||
158
src/Pages/AccountDeletion/DeletionRequestReject.jsx
Normal file
@@ -0,0 +1,158 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useDepositRejectMutation } from "../../../Services/deposit.request.service";
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
comments: yup.string().required("Comment is required"),
|
||||
});
|
||||
|
||||
const DeletionRequestReject = ({ isOpen, onClose, firstField ,id}) => {
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
const toast = useToast()
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
const [ depositReject ] = useDepositRejectMutation()
|
||||
const onSubmit = async(data) => {
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await depositReject({ id ,data})
|
||||
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.error?.data?.message} status={"error"} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
onClose();
|
||||
|
||||
}else if(res?.data?.statusCode === 200) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
onClose();
|
||||
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const selectedFile = event.target.files[0];
|
||||
setFile(selectedFile);
|
||||
};
|
||||
|
||||
|
||||
const { data, isLoading } =
|
||||
(id, {
|
||||
skip: !id,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
reset({
|
||||
investorAmount: data?.data?.investorAmount,
|
||||
});
|
||||
}
|
||||
}, [data, reset]);
|
||||
|
||||
const heandleOnClose = () =>{
|
||||
reset()
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
|
||||
<ModalOverlay />
|
||||
<ModalContent pb={4}>
|
||||
<ModalHeader fontSize={"md"}>Reject</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
{isLoading ? (
|
||||
<FullscreenLoaders height={"50vh"} />
|
||||
) : (
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">Comment</FormLabel>
|
||||
<Textarea
|
||||
rows={6}
|
||||
focusBorderColor="green.400"
|
||||
name="comments"
|
||||
{...register("comments")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="md"
|
||||
placeholder={"Enter your comments...."}
|
||||
rounded={"md"}
|
||||
resize={"none"}
|
||||
/>
|
||||
{errors.comments && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.comments.message}
|
||||
</Text>
|
||||
)}
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
colorScheme="gray"
|
||||
mr={3}
|
||||
onClick={onClose}
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeletionRequestReject;
|
||||
85
src/Pages/AccountDeletion/InvestorComment.jsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Textarea,
|
||||
} from "@chakra-ui/react";
|
||||
import React from "react";
|
||||
|
||||
const InvestorComment = ({ isOpen, onClose }) => {
|
||||
return (
|
||||
<div>
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader fontSize={"md"}></ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<Box as="form">
|
||||
<ModalBody>
|
||||
<FormControl mb={5}>
|
||||
<FormLabel fontSize="sm">Investor Comment</FormLabel>
|
||||
<Textarea
|
||||
rows={6}
|
||||
focusBorderColor="green.400"
|
||||
name="fileName"
|
||||
// {...register("fileName")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="md"
|
||||
placeholder={"The amount of investment is not updated... "}
|
||||
rounded={"md"}
|
||||
resize={"none"}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl mb={4}>
|
||||
<FormLabel fontSize="sm">Admin Commment</FormLabel>
|
||||
<Textarea
|
||||
rows={6}
|
||||
focusBorderColor="green.400"
|
||||
name="fileName"
|
||||
// {...register("fileName")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="md"
|
||||
placeholder={"Enter your comment...."}
|
||||
rounded={"md"}
|
||||
resize={"none"}
|
||||
/>
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter pb={8}>
|
||||
<Button
|
||||
colorScheme="gray"
|
||||
mr={3}
|
||||
onClick={onClose}
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
fontWeight={500}
|
||||
>
|
||||
Approve
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InvestorComment;
|
||||
@@ -5,14 +5,6 @@ import {
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Portal,
|
||||
Select,
|
||||
Switch,
|
||||
Tag,
|
||||
Text,
|
||||
Tooltip,
|
||||
useDisclosure,
|
||||
@@ -22,11 +14,7 @@ import React, { useContext, useEffect, useState, useRef } from "react";
|
||||
import { HiDotsVertical } from "react-icons/hi";
|
||||
import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
|
||||
import {
|
||||
AddIcon,
|
||||
DeleteIcon,
|
||||
EditIcon,
|
||||
EmailIcon,
|
||||
ViewIcon,
|
||||
} from "@chakra-ui/icons";
|
||||
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
|
||||
import DataTable from "../../../Components/DataTable/DataTable";
|
||||
@@ -36,6 +24,7 @@ import CustomAlertDialog from "../../../Components/CustomAlertDialog";
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
import { debounce } from "../../Master/Sponser/AddSponser";
|
||||
import { useGetBankQuery } from "../../../Services/bank.details.service";
|
||||
import NormalTable from '../../../Components/DataTable/NormalTable'
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
|
||||
|
||||
@@ -116,7 +105,7 @@ const BankDetails = () => {
|
||||
});
|
||||
|
||||
|
||||
const extractedArray = filteredData?.map((item) => ({
|
||||
const extractedArray = filteredData?.map((item,index) => ({
|
||||
id: item?.id,
|
||||
"Sr N/O": (
|
||||
<Text
|
||||
@@ -131,18 +120,18 @@ const BankDetails = () => {
|
||||
"Country name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.country_xid}
|
||||
{item?.country?.countryName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Account Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.accountName}
|
||||
{item?.accountName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Account No ": (
|
||||
"Account No": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.accountNumber}
|
||||
@@ -157,16 +146,17 @@ const BankDetails = () => {
|
||||
</Box>
|
||||
),
|
||||
Action: (
|
||||
<Box display={"flex"} justifyContent={"space-between"} gap={2}>
|
||||
<Box display={"flex"} justifyContent={"space-between"}>
|
||||
<Tooltip
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
label="View"
|
||||
label="Edit"
|
||||
bg="#fff"
|
||||
color={"green.500"}
|
||||
placement="top"
|
||||
>
|
||||
<Button
|
||||
bg={index % 2 === 0 ? "#6311cb14" : "#fff"}
|
||||
onClick={() => {
|
||||
navigate(`/bank-details/edit-bank-details/${item.id}`);
|
||||
}}
|
||||
@@ -227,8 +217,8 @@ const BankDetails = () => {
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<DataTable
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
<NormalTable
|
||||
emptyMessage={`We don't have any Details`}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
|
||||
import { Box, useToast } from "@chakra-ui/react";
|
||||
import { useForm } from "react-hook-form";
|
||||
@@ -10,7 +10,8 @@ import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
|
||||
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
|
||||
import SwitchButton from "../../../Components/SwitchButton";
|
||||
import * as yup from "yup";
|
||||
import { useGetBankQuery, useUpdateBankDetailsMutation } from "../../../Services/bank.details.service";
|
||||
import { useGetBankDetailsQuery,useUpdateBankDetailsMutation } from "../../../Services/bank.details.service";
|
||||
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
|
||||
// import { useUpdateBankDetailsMutation, useGetBankQuery } from "../../../Services/investorDetails.service";
|
||||
|
||||
const editBankSchema = yup.object().shape({
|
||||
@@ -28,12 +29,17 @@ const EditBankDetails = () => {
|
||||
const id = params?.id;
|
||||
|
||||
const [isLoadingBtn, setIsLoadingBtn] = useState(false);
|
||||
const { InvestorDetails, setInvestorDetails, slideFromRight } =
|
||||
useContext(GlobalStateContext);
|
||||
const [alert, setAlert] = useState(false);
|
||||
const [form, setForm] = useState({});
|
||||
const [isSwitchOn, setIsSwitchOn] = useState(false);
|
||||
|
||||
const [updateBankDetails] = useUpdateBankDetailsMutation();
|
||||
const { data: bankDetails, error, isLoading } = useGetBankQuery({ id }, { skip: !id });
|
||||
const { data: bankDetails, error, isLoading, refetch } = useGetBankDetailsQuery({ id }, { skip: !id });
|
||||
|
||||
console.log(bankDetails);
|
||||
|
||||
|
||||
const {
|
||||
control,
|
||||
@@ -45,16 +51,22 @@ const EditBankDetails = () => {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (bankDetails) {
|
||||
setInvestorDetails({
|
||||
...bankDetails?.data,
|
||||
});
|
||||
// refetch()
|
||||
if (bankDetails?.data) {
|
||||
reset({
|
||||
accountNumber: bankDetails.accountNumber,
|
||||
swiftCode: bankDetails.swiftCode,
|
||||
bankName: bankDetails.bankName,
|
||||
bankAddress: bankDetails.bankAddress,
|
||||
IBANnumber: bankDetails.IBANnumber,
|
||||
accountName: bankDetails?.data?.accountName,
|
||||
countryName: bankDetails?.data?.countryName,
|
||||
accountNumber: bankDetails?.data?.accountNumber,
|
||||
swiftCode: bankDetails?.data?.swiftCode,
|
||||
bankName: bankDetails?.data?.bankName,
|
||||
bankAddress: bankDetails?.data?.bankAddress,
|
||||
IBANnumber: bankDetails?.data?.IBANnumber,
|
||||
});
|
||||
}
|
||||
}, [bankDetails, reset]);
|
||||
}, [bankDetails, reset,id]);
|
||||
|
||||
if (isLoading) {
|
||||
return <FullscreenLoaders />;
|
||||
@@ -62,7 +74,6 @@ const EditBankDetails = () => {
|
||||
|
||||
const handleConfirm = async () => {
|
||||
setIsLoadingBtn(true);
|
||||
|
||||
try {
|
||||
const formData = {
|
||||
...form,
|
||||
@@ -77,6 +88,7 @@ const EditBankDetails = () => {
|
||||
|
||||
setIsLoadingBtn(false);
|
||||
setAlert(false);
|
||||
refetch()
|
||||
navigate("/bank-details");
|
||||
} else {
|
||||
throw new Error("Something went wrong");
|
||||
@@ -91,66 +103,76 @@ const EditBankDetails = () => {
|
||||
}
|
||||
};
|
||||
|
||||
console.log(bankDetails?.accountNumber);
|
||||
|
||||
|
||||
const formEditFields = [
|
||||
{
|
||||
label: "Country Name",
|
||||
placeHolder: "Enter country name",
|
||||
name: "countryName",
|
||||
type: "text",
|
||||
isRequired: true,
|
||||
section: "Add Details",
|
||||
maxLength: 50,
|
||||
width: "32%",
|
||||
},
|
||||
// {
|
||||
// label: "Country Name",
|
||||
// placeHolder:"",
|
||||
// name: "countryName",
|
||||
// type: "text",
|
||||
// isRequired: true,
|
||||
// section: "Add Details",
|
||||
// maxLength: 50,
|
||||
// width: "32%",
|
||||
// },
|
||||
{
|
||||
label: "Account Name",
|
||||
// value:bankDetails?.accountName,
|
||||
name: "accountName",
|
||||
placeHolder: "Enter account name",
|
||||
placeHolder: "",
|
||||
type: "text",
|
||||
isRequired: true,
|
||||
section: "Add Details",
|
||||
maxLength: 255,
|
||||
// maxLength: 255,
|
||||
width: "32%",
|
||||
},
|
||||
{
|
||||
label: "Account Number *",
|
||||
label: "Account Number",
|
||||
name: "accountNumber",
|
||||
placeHolder: "Enter account number",
|
||||
placeHolder: "",
|
||||
type: "text",
|
||||
section: "Add Details",
|
||||
width: "32%",
|
||||
isRequired: true,
|
||||
},
|
||||
{
|
||||
label: "IBAN Number",
|
||||
name: "IBANnumber",
|
||||
placeHolder: "Enter IBAN number",
|
||||
placeHolder: "",
|
||||
type: "text",
|
||||
section: "Add Details",
|
||||
width: "32%",
|
||||
isRequired: true,
|
||||
},
|
||||
{
|
||||
label: "SWIFT Code",
|
||||
value: bankDetails?.data?.swiftCode,
|
||||
name: "swiftCode",
|
||||
placeHolder: "Enter SWIFT code",
|
||||
placeHolder: "",
|
||||
type: "text",
|
||||
section: "Add Details",
|
||||
width: "32%",
|
||||
isRequired: true,
|
||||
},
|
||||
{
|
||||
label: "Bank Name",
|
||||
name: "bankName",
|
||||
placeHolder: "Enter bank name",
|
||||
placeHolder: "",
|
||||
type: "text",
|
||||
section: "Add Details",
|
||||
width: "32%",
|
||||
isRequired: true,
|
||||
},
|
||||
{
|
||||
label: "Bank Address",
|
||||
name: "bankAddress",
|
||||
placeHolder: "Enter bank address",
|
||||
placeHolder: "",
|
||||
type: "text",
|
||||
section: "Add Details",
|
||||
width: "32%",
|
||||
isRequired: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -163,7 +185,7 @@ const EditBankDetails = () => {
|
||||
return groups;
|
||||
}, {});
|
||||
|
||||
const onSubmit = (data) => {
|
||||
const onSubmit = async(data) => {
|
||||
if (!Object.keys(errors).length) {
|
||||
setForm(data);
|
||||
setAlert(true);
|
||||
|
||||
@@ -20,14 +20,35 @@ import { v4 as uuidv4 } from "uuid";
|
||||
import GlobalStateContext from "../../Contexts/GlobalStateContext";
|
||||
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
||||
import FormInputMain from "../../Components/FormInputMain";
|
||||
import { useGetContactQuery, useUpdateContactMutation } from "../../Services/contact.service";
|
||||
import {
|
||||
useGetContactQuery,
|
||||
useUpdateContactMutation,
|
||||
} from "../../Services/contact.service";
|
||||
import FullscreenLoaders from "../../Components/Loaders/FullscreenLoaders";
|
||||
import ToastBox from "../../Components/ToastBox";
|
||||
|
||||
export const addSponser = yup.object().shape({
|
||||
phoneNumber: yup.string().required("Phone Number is required"),
|
||||
emailAddress: yup.string().required("E-mail ID is required"),
|
||||
websiteUrl: yup.string().required("Website URL is required"),
|
||||
phoneNumber: yup
|
||||
.string()
|
||||
.required("Phone Number is required"),
|
||||
// .matches(
|
||||
// /^\+?[1-9]\d{1,14}$/,
|
||||
// "Phone Number must include a valid ISD code and be in E.164 format"
|
||||
// ),
|
||||
emailAddress: yup
|
||||
.string()
|
||||
.required("E-mail ID is required")
|
||||
.matches(
|
||||
/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
||||
"Invalid email address"
|
||||
),
|
||||
websiteUrl: yup
|
||||
.string()
|
||||
.required("Website URL is required")
|
||||
.matches(
|
||||
/^(https?:\/\/)?([\w.-]+)+(:\d+)?(\/[\w.-]*)*\/?$/,
|
||||
"Invalid URL format"
|
||||
),
|
||||
});
|
||||
|
||||
export function debounce(func, delay) {
|
||||
@@ -39,10 +60,10 @@ export function debounce(func, delay) {
|
||||
}
|
||||
|
||||
const Contact = () => {
|
||||
const toast = useToast()
|
||||
const toast = useToast();
|
||||
const navigate = useNavigate();
|
||||
const [form, setForm] = useState({});
|
||||
const [ isLoading, setIsLoading ] = useState(false)
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
// const { sponser, setSponser } = useContext(GlobalStateContext);
|
||||
const {
|
||||
@@ -52,16 +73,17 @@ const Contact = () => {
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(addSponser),
|
||||
// mode: all
|
||||
});
|
||||
|
||||
|
||||
console.log(errors);
|
||||
|
||||
const {
|
||||
data: contact,
|
||||
isLoading: contactLoading,
|
||||
error,
|
||||
} = useGetContactQuery();
|
||||
const [ updateContact ] = useUpdateContactMutation()
|
||||
|
||||
const [updateContact] = useUpdateContactMutation();
|
||||
|
||||
useEffect(() => {
|
||||
if (contact) {
|
||||
@@ -84,7 +106,7 @@ const Contact = () => {
|
||||
name: "phoneNumber",
|
||||
type: "text",
|
||||
isRequired: true,
|
||||
section: "Add Details",
|
||||
section:"",
|
||||
// value: contact?.phoneNumber || "",
|
||||
},
|
||||
{
|
||||
@@ -93,7 +115,7 @@ const Contact = () => {
|
||||
placeHolder: " ",
|
||||
type: "text",
|
||||
isRequired: true,
|
||||
section: "Add Details",
|
||||
section:"",
|
||||
// value: contact?.emailAddress || "",
|
||||
},
|
||||
{
|
||||
@@ -102,7 +124,7 @@ const Contact = () => {
|
||||
placeHolder: " ",
|
||||
type: "text",
|
||||
isRequired: true,
|
||||
section: "Add Details",
|
||||
section:"",
|
||||
// value: contact?.websiteUrl || "",
|
||||
},
|
||||
];
|
||||
@@ -117,24 +139,20 @@ const Contact = () => {
|
||||
}, {});
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
setIsLoading(true)
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const res = await updateContact(data)
|
||||
const res = await updateContact(data);
|
||||
if (res?.data?.statusCode === 200) {
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.data?.message} />,
|
||||
});
|
||||
setIsLoading(false)
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
setIsLoading(false)
|
||||
|
||||
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
364
src/Pages/Admin/Investor/BankInvestor/BankInvestor.jsx
Normal file
@@ -0,0 +1,364 @@
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
HStack,
|
||||
Input,
|
||||
Select,
|
||||
Text,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
|
||||
import NormalTable from "../../../../Components/DataTable/NormalTable";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import { TABLE_PAGINATION } from "../../../../Constants/Paginations";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
|
||||
import { useGetbanInvestorQuery } from "../../../../Services/ban.investor.service";
|
||||
import { debounce } from "../../../Master/Sponser/AddSponser";
|
||||
import ReasonBanModal from "./ReasonBanModal";
|
||||
import Pagination from "../../../../Components/Pagination";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
|
||||
|
||||
const BankInvestor = () => {
|
||||
const navigate = useNavigate();
|
||||
const toast = useToast();
|
||||
const thirdField = useRef();
|
||||
const { bankInvestor, setBankInvestor, slideFromRight } =
|
||||
useContext(GlobalStateContext);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
const { isOpen: isOpen, onOpen: onOpen, onClose: onClose } = useDisclosure();
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
// =========================== [Use State] =============================
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
|
||||
const [status, setStatus] = useState("");
|
||||
const [kyc, setKyc] = useState("");
|
||||
const [country, setCountry] = useState("");
|
||||
|
||||
// Debounce the search term to avoid making a request on every keystroke
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedSearchTerm(searchTerm);
|
||||
}, 500); // Adjust delay as needed
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [searchTerm]);
|
||||
|
||||
const {
|
||||
data,
|
||||
isLoading: unbanLoading,
|
||||
error,
|
||||
refetch,
|
||||
} = useGetbanInvestorQuery(
|
||||
{
|
||||
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
|
||||
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
|
||||
search: debouncedSearchTerm,
|
||||
KYCStatus: kyc,
|
||||
country_xid: country,
|
||||
},
|
||||
{
|
||||
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr N/O",
|
||||
"Date",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Country",
|
||||
"Phone Number",
|
||||
"E-mail ID",
|
||||
"KYC Status",
|
||||
"Action",
|
||||
];
|
||||
|
||||
const handleUpdateStatus = debounce((id) => {
|
||||
setBankInvestor((prevData) =>
|
||||
prevData.map((bankInvestor) =>
|
||||
bankInvestor.id === id ? { ...bankInvestor } : bankInvestor
|
||||
)
|
||||
);
|
||||
toast({
|
||||
render: () => <ToastBox message={"Status changed succesfully.!"} />,
|
||||
});
|
||||
}, 300);
|
||||
|
||||
// ====================================================[Table Filter]================================================================
|
||||
const filteredData = data?.data?.rows?.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item?.clientReference_id;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name?.toLowerCase().includes(searchLower);
|
||||
|
||||
// Filter by status
|
||||
// const status = item.status;
|
||||
// const statusLower = status ? "active" : "inactive";
|
||||
|
||||
// const statusMatches =
|
||||
// statusFilter === "all" ||
|
||||
// (statusFilter === "active" && status === true) ||
|
||||
// (statusFilter === "inactive" && status === false);
|
||||
|
||||
return nameMatches;
|
||||
});
|
||||
|
||||
const extractedArray = data?.data?.rows?.map((item) => ({
|
||||
id: item?.id,
|
||||
"Sr N/O": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center fw-bold web-text-small"
|
||||
>
|
||||
{item.id}
|
||||
</Text>
|
||||
),
|
||||
Date: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{formatDate(item?.date)}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Client ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.clientReference_id}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"First Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
Country: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.country}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.phoneNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"E-mail ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.emailAddress}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"KYC Status": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Badge
|
||||
fontWeight={"500"}
|
||||
textTransform={"none"}
|
||||
color={item?.KYCStatus === false ? "red" : "blue"}
|
||||
px={2}
|
||||
py={0.5}
|
||||
variant={"ghost"}
|
||||
>
|
||||
{item?.KYCStatus === true ? "Completed" : "Incompleted"}
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
Action: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Badge
|
||||
cursor={"pointer"}
|
||||
fontWeight={"500"}
|
||||
textTransform={"none"}
|
||||
colorScheme={"red"}
|
||||
px={2}
|
||||
py={0.5}
|
||||
onClick={() => {
|
||||
setActionId(item?.id);
|
||||
onOpen();
|
||||
}}
|
||||
>
|
||||
Ban Investor
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedInvestorDetails = InvestorDetails.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setInvestorDetails(updatedInvestorDetails);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
const handleEdit = (id) => {
|
||||
setActionId(id);
|
||||
onEditOpen();
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
ps={1}
|
||||
pe={1}
|
||||
pb={4}
|
||||
pt={4}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
mt={1}
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<HStack display={"flex"} alignItems={"center"}>
|
||||
<Pagination
|
||||
isLoading={unbanLoading}
|
||||
pageSize={pageSize}
|
||||
setPageSize={setPageSize}
|
||||
currentPage={currentPage}
|
||||
setCurrentPage={setCurrentPage}
|
||||
totalItems={data?.data?.totalItems}
|
||||
/>
|
||||
{/* <Select
|
||||
focusBorderColor="green.500"
|
||||
size={"sm"}
|
||||
fontSize={"xs"}
|
||||
cursor={"pointer"}
|
||||
onChange={(e) => setStatus(e.target.value)}
|
||||
value={status}
|
||||
>
|
||||
<option value="" defaultValue={""} disabled hidden>
|
||||
Status
|
||||
</option>
|
||||
|
||||
<option value="">Status</option>
|
||||
<option value="0">Ban</option>
|
||||
<option value="1">UnBan</option>
|
||||
</Select> */}
|
||||
|
||||
<Select
|
||||
focusBorderColor="green.500"
|
||||
size={"sm"}
|
||||
fontSize={"xs"}
|
||||
cursor={"pointer"}
|
||||
onChange={(e) => setKyc(e.target.value)}
|
||||
value={kyc}
|
||||
>
|
||||
<option value="" defaultValue={""} disabled hidden>
|
||||
KYC Status
|
||||
</option>
|
||||
<option value="">KYC Status</option>
|
||||
<option value="0">Incompleted</option>
|
||||
<option value="1">Completed</option>
|
||||
</Select>
|
||||
|
||||
<Select
|
||||
focusBorderColor="green.500"
|
||||
size={"sm"}
|
||||
fontSize={"xs"}
|
||||
cursor={"pointer"}
|
||||
onChange={(e) => setCountry(e.target.value)}
|
||||
value={country}
|
||||
>
|
||||
<option value="" defaultValue={""} disabled hidden>
|
||||
Country
|
||||
</option>
|
||||
<option value="">All</option>
|
||||
<option value="1">Behrain</option>
|
||||
<option value="2">Kuwait</option>
|
||||
<option value="3">Oman</option>
|
||||
<option value="4">Qatar</option>
|
||||
<option value="5">Saudi arabia</option>
|
||||
<option value="6">United arab emirates</option>
|
||||
</Select>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<ReasonBanModal isOpen={isOpen} onClose={onClose} id={actionId} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default BankInvestor;
|
||||
27
src/Pages/Admin/Investor/BankInvestor/Investor.jsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Box, Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react";
|
||||
import React from "react";
|
||||
import BankInvestor from "./BankInvestor";
|
||||
import UnbanInvestor from "../UnbanInvestor/UnbanInvestor";
|
||||
|
||||
const Investor = () => {
|
||||
return (
|
||||
<Box overflowY={"scroll"} height={"100vh"}>
|
||||
<Tabs colorScheme="green" mt={3}>
|
||||
<TabList>
|
||||
<Tab fontSize={"sm"}>Unban Investor</Tab>
|
||||
<Tab fontSize={"sm"}>Ban Investor</Tab>
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<UnbanInvestor />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<BankInvestor />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Investor;
|
||||
143
src/Pages/Admin/Investor/BankInvestor/ReasonBanModal.jsx
Normal file
@@ -0,0 +1,143 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import { useDepositRejectMutation } from "../../../../Services/drawal.request.service";
|
||||
import { useUpdateBanMutation } from "../../../../Services/ban.investor.service";
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
comments: yup.string().required("Comment is required"),
|
||||
});
|
||||
|
||||
const ReasonBanModal = ({ isOpen, onClose, firstField ,id}) => {
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
const [ updateBanMutation ] = useUpdateBanMutation()
|
||||
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await updateBanMutation({ id ,data})
|
||||
console.log(res);
|
||||
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.error?.data?.message} status={"error"} />
|
||||
),
|
||||
});
|
||||
heandleOnClose()
|
||||
|
||||
}else if(res?.data?.statusCode === 200) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.data?.message} />
|
||||
),
|
||||
});
|
||||
heandleOnClose()
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
const heandleOnClose = () =>{
|
||||
reset()
|
||||
onClose()
|
||||
setIsBtnLoading(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
|
||||
<ModalOverlay />
|
||||
<ModalContent pb={4}>
|
||||
<ModalHeader fontSize={"md"}>Reason for Unban</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">Comment</FormLabel>
|
||||
<Textarea
|
||||
rows={6}
|
||||
focusBorderColor="green.400"
|
||||
name="comments"
|
||||
{...register("comments")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="md"
|
||||
placeholder={"Enter your comments...."}
|
||||
rounded={"md"}
|
||||
resize={"none"}
|
||||
/>
|
||||
{errors.comments && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.comments.message}
|
||||
</Text>
|
||||
)}
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
colorScheme="gray"
|
||||
mr={3}
|
||||
onClick={onClose}
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
fontWeight={400}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReasonBanModal;
|
||||
|
||||
147
src/Pages/Admin/Investor/UnbanInvestor/ReasonBanModal.jsx
Normal file
@@ -0,0 +1,147 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import { useDepositRejectMutation } from "../../../../Services/drawal.request.service";
|
||||
import { useUpdateBanMutation, useUpdateUnbanMutation } from "../../../../Services/ban.investor.service";
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
comments: yup.string().required("Comment is required"),
|
||||
});
|
||||
|
||||
const ReasonBanModal = ({ isOpen, onClose, firstField ,id}) => {
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
const [ updateBan ] = useUpdateUnbanMutation()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
console.log(data);
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await updateBan({ id ,data})
|
||||
console.log(res);
|
||||
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.error?.data?.message} status={"error"} />
|
||||
),
|
||||
});
|
||||
heandleOnClose()
|
||||
|
||||
}else if(res?.data) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.data?.message} />
|
||||
),
|
||||
});
|
||||
heandleOnClose()
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
const heandleOnClose = () =>{
|
||||
reset()
|
||||
onClose()
|
||||
setIsBtnLoading(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
|
||||
<ModalOverlay />
|
||||
<ModalContent pb={4}>
|
||||
<ModalHeader fontSize={"md"}>Reason for Ban</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">Comment</FormLabel>
|
||||
<Textarea
|
||||
rows={6}
|
||||
focusBorderColor="green.400"
|
||||
name="comments"
|
||||
{...register("comments")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="md"
|
||||
placeholder={"Enter your comments...."}
|
||||
rounded={"md"}
|
||||
resize={"none"}
|
||||
/>
|
||||
{errors.comments && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.comments.message}
|
||||
</Text>
|
||||
)}
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
colorScheme="gray"
|
||||
mr={3}
|
||||
onClick={onClose}
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
fontWeight={400}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReasonBanModal;
|
||||
|
||||
355
src/Pages/Admin/Investor/UnbanInvestor/UnbanInvestor.jsx
Normal file
@@ -0,0 +1,355 @@
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
HStack,
|
||||
Input,
|
||||
Select,
|
||||
Text,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
|
||||
import DataTable from "../../../../Components/DataTable/NormalTable";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import { generateSerialNumber } from "../../../../Constants/Constants";
|
||||
import { TABLE_PAGINATION } from "../../../../Constants/Paginations";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
|
||||
import { useGetUnbanInvestorQuery } from "../../../../Services/ban.investor.service";
|
||||
import { debounce } from "../../../Master/Sponser/AddSponser";
|
||||
import ReasonBanModal from "./ReasonBanModal";
|
||||
import Pagination from "../../../../Components/Pagination";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
|
||||
|
||||
const UnbanInvestor = () => {
|
||||
const navigate = useNavigate();
|
||||
const toast = useToast();
|
||||
const thirdField = useRef();
|
||||
const { bankInvestor, setBankInvestor, slideFromRight } =
|
||||
useContext(GlobalStateContext);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState("");
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
const { isOpen: isOpen, onOpen: onOpen, onClose: onClose } = useDisclosure();
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
// =========================== [Use State] =============================
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
|
||||
const [status, setStatus] = useState("");
|
||||
const [kyc, setKyc] = useState("");
|
||||
const [country, setCountry] = useState("");
|
||||
|
||||
// Debounce the search term to avoid making a request on every keystroke
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedSearchTerm(searchTerm);
|
||||
}, 500); // Adjust delay as needed
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [searchTerm]);
|
||||
|
||||
const {
|
||||
data,
|
||||
isLoading: unbanLoading,
|
||||
error,
|
||||
refetch,
|
||||
} = useGetUnbanInvestorQuery(
|
||||
{
|
||||
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
|
||||
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
|
||||
searchTerm: debouncedSearchTerm,
|
||||
KYCStatus: kyc,
|
||||
country_xid: country,
|
||||
},
|
||||
{
|
||||
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr N/O",
|
||||
"Date",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Country",
|
||||
"Phone Number",
|
||||
"E-mail ID",
|
||||
"KYC Status",
|
||||
"Action",
|
||||
];
|
||||
|
||||
const handleUpdateStatus = debounce((id) => {
|
||||
setBankInvestor((prevData) =>
|
||||
prevData.map((bankInvestor) =>
|
||||
bankInvestor.id === id ? { ...bankInvestor } : bankInvestor
|
||||
)
|
||||
);
|
||||
toast({
|
||||
render: () => <ToastBox message={"Status changed succesfully.!"} />,
|
||||
});
|
||||
}, 300);
|
||||
|
||||
// ====================================================[Table Filter]================================================================
|
||||
const filteredData = data?.data?.rows?.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item?.clientReference_id;
|
||||
const searchLower = searchTerm?.toLowerCase();
|
||||
const nameMatches = name?.toLowerCase().includes(searchLower);
|
||||
|
||||
return nameMatches;
|
||||
});
|
||||
|
||||
const extractedArray = data?.data?.rows?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr N/O": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center fw-bold web-text-small"
|
||||
>
|
||||
{generateSerialNumber(index, currentPage, pageSize)}
|
||||
</Text>
|
||||
),
|
||||
Date: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{formatDate(item?.date)}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Client ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.clientReference_id}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"First Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
Country: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.country}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.phoneNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"E-mail ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.emailAddress}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"KYC Status": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Badge
|
||||
fontWeight={"500"}
|
||||
textTransform={"none"}
|
||||
color={item?.KYCStatus === false ? "red" : "blue"}
|
||||
px={2}
|
||||
py={0.5}
|
||||
variant={"ghost"}
|
||||
>
|
||||
{item?.KYCStatus === true ? "Completed" : "Incompleted"}
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
Action: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Badge
|
||||
cursor={"pointer"}
|
||||
fontWeight={"500"}
|
||||
textTransform={"none"}
|
||||
colorScheme={"red"}
|
||||
px={2}
|
||||
py={0.5}
|
||||
onClick={() => {
|
||||
setActionId(item?.id);
|
||||
onOpen();
|
||||
}}
|
||||
>
|
||||
Ban Investor
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedInvestorDetails = InvestorDetails.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setInvestorDetails(updatedInvestorDetails);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
const handleEdit = (id) => {
|
||||
setActionId(id);
|
||||
onEditOpen();
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
ps={1}
|
||||
pe={1}
|
||||
pb={4}
|
||||
pt={4}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
mt={1}
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
<HStack display={"flex"} alignItems={"center"}>
|
||||
<Pagination
|
||||
isLoading={unbanLoading}
|
||||
pageSize={pageSize}
|
||||
setPageSize={setPageSize}
|
||||
currentPage={currentPage}
|
||||
setCurrentPage={setCurrentPage}
|
||||
totalItems={data?.data?.totalItems}
|
||||
/>
|
||||
{/* <Select
|
||||
focusBorderColor="green.500"
|
||||
size={"sm"}
|
||||
fontSize={"xs"}
|
||||
cursor={"pointer"}
|
||||
onChange={(e) => setStatus(e.target.value)}
|
||||
value={status}
|
||||
>
|
||||
<option value="" defaultValue={""} disabled hidden>
|
||||
Status
|
||||
</option>
|
||||
|
||||
<option value="">Status</option>
|
||||
<option value="0">Ban</option>
|
||||
<option value="1">UnBan</option>
|
||||
</Select> */}
|
||||
|
||||
<Select
|
||||
focusBorderColor="green.500"
|
||||
size={"sm"}
|
||||
fontSize={"xs"}
|
||||
cursor={"pointer"}
|
||||
onChange={(e) => setKyc(e.target.value)}
|
||||
value={kyc}
|
||||
>
|
||||
<option value="" defaultValue={""} disabled hidden>
|
||||
KYC Status
|
||||
</option>
|
||||
<option value="">KYC Status</option>
|
||||
<option value="0">Incompleted</option>
|
||||
<option value="1">Completed</option>
|
||||
</Select>
|
||||
|
||||
<Select
|
||||
focusBorderColor="green.500"
|
||||
size={"sm"}
|
||||
fontSize={"xs"}
|
||||
cursor={"pointer"}
|
||||
onChange={(e) => setCountry(e.target.value)}
|
||||
value={country}
|
||||
>
|
||||
<option value="" defaultValue={""} disabled hidden>
|
||||
Country
|
||||
</option>
|
||||
<option value="">All</option>
|
||||
<option value="1">Behrain</option>
|
||||
<option value="2">Kuwait</option>
|
||||
<option value="3">Oman</option>
|
||||
<option value="4">Qatar</option>
|
||||
<option value="5">Saudi arabia</option>
|
||||
<option value="6">United arab emirates</option>
|
||||
</Select>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<DataTable
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<ReasonBanModal isOpen={isOpen} onClose={onClose} id={actionId} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default UnbanInvestor;
|
||||
@@ -1,22 +1,404 @@
|
||||
import { Box, Image, Text } from "@chakra-ui/react"
|
||||
// import error from "../assets/Error.svg"
|
||||
import robot from "../../assets/robot.png"
|
||||
// import robot from "../assets/robot.png"
|
||||
const Notification = () => {
|
||||
return (
|
||||
|
||||
<Box
|
||||
h={'100vh'}
|
||||
display={'flex'}
|
||||
justifyContent={'center'}
|
||||
alignItems={'center'}
|
||||
flexDirection={'column'}
|
||||
gap={8}
|
||||
>
|
||||
<Image src={robot} w={"171px"} />
|
||||
{/* <Text color={'green.800'} as={'span'} fontSize={'small'}>The requested URL was not found on this server.</Text> */}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Select,
|
||||
Text,
|
||||
Tooltip,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import * as yup from "yup";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
||||
import FormInputMain from "../../Components/FormInputMain";
|
||||
import {
|
||||
useGetContactQuery,
|
||||
useSendNotificationMutation,
|
||||
useUpdateContactMutation,
|
||||
} from "../../Services/contact.service";
|
||||
import FullscreenLoaders from "../../Components/Loaders/FullscreenLoaders";
|
||||
import ToastBox from "../../Components/ToastBox";
|
||||
import NormalTable from "../../Components/DataTable/NormalTable";
|
||||
import GlobalStateContext from "../../Contexts/GlobalStateContext";
|
||||
import { useGetInvestorsQuery } from "../../Services/investor.details.service";
|
||||
import {
|
||||
INVESTOR_TABLE_PAGINATION,
|
||||
TABLE_PAGINATION,
|
||||
} from "../../Constants/Paginations";
|
||||
import { formatDate, generateSerialNumber } from "../../Constants/Constants";
|
||||
import { ViewIcon } from "@chakra-ui/icons";
|
||||
import { useGetUnbanInvestorQuery } from "../../Services/ban.investor.service";
|
||||
|
||||
export default Notification
|
||||
export const notification = yup.object().shape({
|
||||
title: yup.string().required("Notification Header is required"),
|
||||
ManualDate: yup
|
||||
.date()
|
||||
.required("Manual Date is required")
|
||||
.typeError("Invalid date format"),
|
||||
ManualTime: yup
|
||||
.string()
|
||||
.required("Manual Time is required")
|
||||
.matches(
|
||||
/^([01]\d|2[0-3]):?([0-5]\d)$/,
|
||||
"Invalid time format, must be in HH:mm"
|
||||
),
|
||||
expectedReturn: yup.string().required("Expected Return is required"),
|
||||
});
|
||||
|
||||
export const notificationNew = yup.object().shape({
|
||||
title: yup.string().required("Notification Header is required"),
|
||||
message: yup.string().notRequired(),
|
||||
});
|
||||
|
||||
const Notification = () => {
|
||||
const toast = useToast();
|
||||
const navigate = useNavigate();
|
||||
const [form, setForm] = useState({});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [selectedRadio, setSelectedRadio] = useState([]);
|
||||
const [pageSize, setPageSize] = useState(INVESTOR_TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(
|
||||
INVESTOR_TABLE_PAGINATION?.page
|
||||
);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
const [country, setCountry] = useState("");
|
||||
const [kyc, setKyc] = useState("");
|
||||
|
||||
const {
|
||||
control,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(notificationNew),
|
||||
|
||||
defaultValues: {
|
||||
title: "",
|
||||
message: "",
|
||||
},
|
||||
});
|
||||
|
||||
console.log(errors);
|
||||
|
||||
const {
|
||||
data: contact,
|
||||
isLoading: contactLoading,
|
||||
error,
|
||||
} = useGetContactQuery();
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
// const {
|
||||
// data: investorDetails,
|
||||
// isLoading: investorDetailsLoading,
|
||||
// // error,
|
||||
// } = useGetInvestorsQuery({ page: currentPage, size: pageSize });
|
||||
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedSearchTerm(searchTerm.trim()); // Trim to remove leading/trailing spaces
|
||||
}, 300);
|
||||
return () => clearTimeout(handler);
|
||||
}, [searchTerm]);
|
||||
|
||||
const {
|
||||
data: investorDetails,
|
||||
isLoading: investorDetailsLoading,
|
||||
refetch,
|
||||
} = useGetUnbanInvestorQuery(
|
||||
{
|
||||
page: 1, // Omit pagination for search
|
||||
size: 10000, // Omit pagination for search
|
||||
// page: debouncedSearchTerm ? undefined : currentPage, // Disable pagination for search
|
||||
// size: debouncedSearchTerm ? undefined : 10000 || pageSize || 500, // Disable pagination for search
|
||||
search: debouncedSearchTerm, // Pass search term
|
||||
country_xid: country,
|
||||
KYCStatus: kyc,
|
||||
},
|
||||
{
|
||||
skip: searchTerm !== "" && debouncedSearchTerm === "", // Skip if search not debounced yet
|
||||
}
|
||||
);
|
||||
|
||||
// useEffect(() => {
|
||||
// console.log("Search Term:", searchTerm);
|
||||
// console.log("Debounced Search Term:", debouncedSearchTerm);
|
||||
// console.log("Investor Details:", investorDetails);
|
||||
// }, [searchTerm, debouncedSearchTerm, investorDetails]);
|
||||
|
||||
const [sendNotification] = useSendNotificationMutation();
|
||||
|
||||
if (contactLoading) {
|
||||
return <FullscreenLoaders />;
|
||||
}
|
||||
|
||||
const formFields = [
|
||||
{
|
||||
label: "Notification Header",
|
||||
placeHolder: " ",
|
||||
name: "title",
|
||||
type: "text",
|
||||
width: "100%",
|
||||
maxLength: 100,
|
||||
helperText: `Maximum length should be 100 characters. You have entered ${
|
||||
watch()?.title?.length || 0
|
||||
} characters.`,
|
||||
isRequired: true,
|
||||
section: "Send Custom Push Notification",
|
||||
// value: contact?.phoneNumber || "",
|
||||
},
|
||||
{
|
||||
label: "Notification Message",
|
||||
placeHolder: " ",
|
||||
name: "message",
|
||||
width: "100%",
|
||||
type: "textarea",
|
||||
isRequired: true,
|
||||
maxLength: 200,
|
||||
helperText: `Maximum length should be 200 characters. You have entered ${
|
||||
watch()?.message?.length || 0
|
||||
} characters.`,
|
||||
section: "Send Custom Push Notification",
|
||||
// value: contact?.phoneNumber || "",
|
||||
},
|
||||
];
|
||||
|
||||
const groupedFields = formFields.reduce((groups, field) => {
|
||||
const { section } = field;
|
||||
if (!groups[section]) {
|
||||
groups[section] = [];
|
||||
}
|
||||
groups[section].push(field);
|
||||
return groups;
|
||||
}, {});
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
const dataToPass = {
|
||||
...data,
|
||||
principal_xid: selectedRadio,
|
||||
};
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const res = await sendNotification(dataToPass);
|
||||
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
} else if (res?.data) {
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.data?.message} />,
|
||||
});
|
||||
setIsLoading(false);
|
||||
setSelectedRadio([]);
|
||||
reset({
|
||||
title: "", // Resetting specific fields
|
||||
message: "",
|
||||
}); // Clears the form fields
|
||||
} else {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={"Something went wrong"} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr N/O",
|
||||
"Date",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Country",
|
||||
"Phone Number",
|
||||
"E-mail ID",
|
||||
"KYC Status",
|
||||
];
|
||||
|
||||
const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({
|
||||
id: item?.principal_xid,
|
||||
"Sr N/O": (
|
||||
<Text
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center fw-bold web-text-small"
|
||||
>
|
||||
{generateSerialNumber(idx, currentPage, pageSize)}
|
||||
</Text>
|
||||
),
|
||||
Date: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{formatDate(item?.date)}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Client ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.clientReference_id}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"First Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
Country: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.country}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.phoneNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"E-mail ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.emailAddress}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"KYC Status": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Badge
|
||||
fontWeight={"500"}
|
||||
textTransform={"none"}
|
||||
color={item?.KYCStatus === false ? "red" : "blue"}
|
||||
px={2}
|
||||
py={0.5}
|
||||
variant={"ghost"}
|
||||
>
|
||||
{item?.KYCStatus === true ? "Completed" : "Not Completed"}
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={14}>
|
||||
<FormInputMain
|
||||
groupedFields={groupedFields}
|
||||
control={control}
|
||||
errors={errors}
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
btnLoading={isLoading}
|
||||
>
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
ps={1}
|
||||
pe={1}
|
||||
pb={4}
|
||||
pt={4}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
mt={1}
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
<HStack className="col" justifyContent={"end"}>
|
||||
<Select
|
||||
w={250}
|
||||
focusBorderColor="green.500"
|
||||
size={"sm"}
|
||||
fontSize={"xs"}
|
||||
cursor={"pointer"}
|
||||
onChange={(e) => setCountry(e.target.value)}
|
||||
value={country}
|
||||
>
|
||||
<option value="" defaultValue={""} disabled hidden>
|
||||
Country
|
||||
</option>
|
||||
<option value="">All</option>
|
||||
<option value="1">Bahrain</option>
|
||||
<option value="2">Kuwait</option>
|
||||
<option value="3">Oman</option>
|
||||
<option value="4">Qatar</option>
|
||||
<option value="5">Saudi arabia</option>
|
||||
<option value="6">United arab emirates</option>
|
||||
</Select>
|
||||
<Select
|
||||
w={250}
|
||||
focusBorderColor="green.500"
|
||||
size={"sm"}
|
||||
fontSize={"xs"}
|
||||
cursor={"pointer"}
|
||||
onChange={(e) => setKyc(e.target.value)}
|
||||
value={kyc}
|
||||
>
|
||||
<option value="" defaultValue={""} disabled hidden>
|
||||
KYC Status
|
||||
</option>
|
||||
<option value="">KYC Status</option>
|
||||
<option value="0">Not Completed</option>
|
||||
<option value="1">Completed</option>
|
||||
</Select>
|
||||
</HStack>
|
||||
</HStack>
|
||||
<Box overflow={"scroll"} h={"58vh"}>
|
||||
<NormalTable
|
||||
centered={true}
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={investorDetailsLoading}
|
||||
setSelectedRadio={setSelectedRadio}
|
||||
selectedRadio={selectedRadio}
|
||||
showRadioButton={true}
|
||||
/>
|
||||
</Box>
|
||||
</FormInputMain>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Notification;
|
||||
|
||||
@@ -1,22 +1,241 @@
|
||||
import { Box, Image, Text } from "@chakra-ui/react"
|
||||
// import error from "../assets/Error.svg"
|
||||
import robot from "../../assets/robot.png"
|
||||
// import robot from "../assets/robot.png"
|
||||
const Users = () => {
|
||||
return (
|
||||
|
||||
<Box
|
||||
h={'100vh'}
|
||||
display={'flex'}
|
||||
justifyContent={'center'}
|
||||
alignItems={'center'}
|
||||
flexDirection={'column'}
|
||||
gap={8}
|
||||
>
|
||||
<Image src={robot} w={"171px"} />
|
||||
{/* <Text color={'green.800'} as={'span'} fontSize={'small'}>The requested URL was not found on this server.</Text> */}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Text,
|
||||
Tooltip,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useState, useRef } from "react";
|
||||
import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
|
||||
import {EditIcon,} from "@chakra-ui/icons";
|
||||
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
||||
import DataTable from "../../Components/DataTable/NormalTable";
|
||||
import GlobalStateContext from "../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../Components/CustomAlertDialog";
|
||||
import ToastBox from "../../Components/ToastBox";
|
||||
import { debounce } from "./Contact";
|
||||
|
||||
export default Users
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
|
||||
|
||||
const Users = () => {
|
||||
const navigate = useNavigate();
|
||||
const toast = useToast();
|
||||
const thirdField = useRef();
|
||||
const { users, setUsers, slideFromRight } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
// const {
|
||||
// isOpen: isEditOpen,
|
||||
// onOpen: onEditOpen,
|
||||
// onClose: onEditClose,
|
||||
// } = useDisclosure();
|
||||
|
||||
const btnRef = React.useRef();
|
||||
|
||||
// const {
|
||||
// data: bankDetails,
|
||||
// isLoading: bankDetailsLoading,
|
||||
// error,
|
||||
// } = useGetBankQuery({ page: 1, size: 10 });
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr N/O",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"E-mail ID",
|
||||
"Role",
|
||||
"Phone Number",
|
||||
"Action",
|
||||
];
|
||||
|
||||
const handleUpdateStatus = debounce((id) => {
|
||||
setUsers((prevData) =>
|
||||
prevData.map((users) =>
|
||||
users.id === id ? { ...users } : users
|
||||
)
|
||||
);
|
||||
toast({
|
||||
render: () => <ToastBox message={"Status changed succesfully.!"} />,
|
||||
});
|
||||
}, 300);
|
||||
|
||||
// ====================================================[Table Filter]================================================================
|
||||
const filteredData = users?.data?.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item.emailID;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name?.toLowerCase().includes(searchLower);
|
||||
|
||||
// Filter by status
|
||||
// const status = item.status;
|
||||
// const statusLower = status ? "active" : "inactive";
|
||||
|
||||
// const statusMatches =
|
||||
// statusFilter === "all" ||
|
||||
// (statusFilter === "active" && status === true) ||
|
||||
// (statusFilter === "inactive" && status === false);
|
||||
|
||||
return nameMatches;
|
||||
});
|
||||
|
||||
|
||||
const extractedArray = filteredData?.map((item) => ({
|
||||
id: item?.id,
|
||||
"Sr N/O": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center fw-bold web-text-small"
|
||||
>
|
||||
{item.id}
|
||||
</Text>
|
||||
),
|
||||
"First Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"E-mail ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.emailID}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Role": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.role}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.phoneNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
Action: (
|
||||
<Box display={"flex"} justifyContent={"space-between"} gap={2}>
|
||||
<Tooltip
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
label="View"
|
||||
bg="#fff"
|
||||
color={"green.500"}
|
||||
placement="top"
|
||||
>
|
||||
<Button
|
||||
onClick={() => {
|
||||
navigate(`/bank-details/edit-bank-details/${item.id}`);
|
||||
}}
|
||||
_hover={{ color: "green.500" }}
|
||||
// transition={"0.5s all"}
|
||||
color="green.300"
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
>
|
||||
<EditIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedInvestorDetails = InvestorDetails.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setInvestorDetails(updatedInvestorDetails);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
const handleEdit = (id) => {
|
||||
setActionId(id);
|
||||
onEditOpen();
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
ps={1}
|
||||
pe={1}
|
||||
pb={4}
|
||||
pt={4}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
mt={1}
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<DataTable
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Users;
|
||||
|
||||
463
src/Pages/BankDepositRequest/BankDepositRequest.jsx
Normal 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;
|
||||
149
src/Pages/BankDepositRequest/ConfirmModal.jsx
Normal 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;
|
||||
139
src/Pages/BankDepositRequest/RejectModal.jsx
Normal 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;
|
||||
245
src/Pages/ChangePassword.jsx
Normal 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;
|
||||
@@ -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>
|
||||
)
|
||||
504
src/Pages/Dashboard/Dashbaord.jsx
Normal 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;
|
||||
169
src/Pages/Dashboard/InvestmentOpportunities.jsx
Normal 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’s</option>
|
||||
<option value="last30days">Last 30 day’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;
|
||||
@@ -35,17 +35,18 @@ import NormalTable from "../../../Components/DataTable/NormalTable";
|
||||
import { useGetDepositRequestQuery } from "../../../Services/deposit.request.service";
|
||||
import { current } from "@reduxjs/toolkit";
|
||||
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
|
||||
import { removeTrailingZeros } from "../../../Constants/Constants";
|
||||
import { formatCurrency } from "../../../Components/CurrencyInput";
|
||||
import {
|
||||
generateSerialNumber,
|
||||
removeTrailingZeros,
|
||||
} from "../../../Constants/Constants";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
|
||||
export const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
|
||||
|
||||
const DepositRequest = () => {
|
||||
const navigate = useNavigate();
|
||||
const toast = useToast();
|
||||
const { depositRequest, setDepositRequest, slideFromRight } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState("");
|
||||
@@ -61,27 +62,62 @@ const DepositRequest = () => {
|
||||
onOpen: onRejectOpen,
|
||||
onClose: onRejectClose,
|
||||
} = useDisclosure();
|
||||
|
||||
|
||||
|
||||
// =========================== [Use State] =============================
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
|
||||
// Debounce the search term to avoid making a request on every keystroke
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedSearchTerm(searchTerm);
|
||||
}, 500); // Adjust delay as needed
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [searchTerm]);
|
||||
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
const {
|
||||
data,
|
||||
isLoading: depositRequestLoading,
|
||||
error,
|
||||
} = useGetDepositRequestQuery({ page: currentPage, size: pageSize });
|
||||
|
||||
refetch,
|
||||
} = useGetDepositRequestQuery({
|
||||
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
|
||||
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
|
||||
search: debouncedSearchTerm,
|
||||
},
|
||||
{
|
||||
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
|
||||
});
|
||||
|
||||
// Use useEffect to refetch data when the component mounts
|
||||
useEffect(() => {
|
||||
refetch();
|
||||
}, [refetch]);
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
// "Sr.no",
|
||||
"Sr.no",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Country",
|
||||
"Phone Number",
|
||||
"Amount in Investor currency",
|
||||
"Deposit Amount",
|
||||
"Deposit Date",
|
||||
"Action",
|
||||
];
|
||||
@@ -100,40 +136,39 @@ const DepositRequest = () => {
|
||||
}, 300);
|
||||
|
||||
const filteredData = data?.data?.rows
|
||||
.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = [item.firstName, item.lastName, item.countryName].filter(Boolean).join(' ');
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = [item.firstName, item.lastName, item.countryName]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
|
||||
// Filter by status (Uncomment and use if needed)
|
||||
// const status = item.status;
|
||||
// const statusLower = status ? "active" : "inactive";
|
||||
// Filter by status (Uncomment and use if needed)
|
||||
// const status = item.status;
|
||||
// const statusLower = status ? "active" : "inactive";
|
||||
|
||||
// const statusMatches =
|
||||
// statusFilter === "all" ||
|
||||
// (statusFilter === "active" && status === true) ||
|
||||
// (statusFilter === "inactive" && status === false);
|
||||
// const statusMatches =
|
||||
// statusFilter === "all" ||
|
||||
// (statusFilter === "active" && status === true) ||
|
||||
// (statusFilter === "inactive" && status === false);
|
||||
|
||||
return nameMatches;
|
||||
})
|
||||
.sort((b, a) => new Date(a.createdAt) - new Date(b.createdAt));
|
||||
return nameMatches;
|
||||
})
|
||||
.sort((b, a) => new Date(a.createdAt) - new Date(b.createdAt));
|
||||
|
||||
console.log(data?.data?.rows);
|
||||
|
||||
|
||||
const extractedArray = filteredData?.map((item, index) => ({
|
||||
const extractedArray = data?.data?.rows?.map((item, idx) => ({
|
||||
// id: item?.id,
|
||||
"Sr.no": (
|
||||
<Text
|
||||
w={"30px"}
|
||||
w={"20px"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{index + 1}
|
||||
{generateSerialNumber(idx, currentPage, pageSize)}
|
||||
</Text>
|
||||
),
|
||||
"Client ID": (
|
||||
@@ -149,7 +184,7 @@ const DepositRequest = () => {
|
||||
</Text>
|
||||
),
|
||||
"First Name": (
|
||||
<Box isTruncated={true} w={"70px"}>
|
||||
<Box isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.firstName}
|
||||
</Text>
|
||||
@@ -176,10 +211,22 @@ const DepositRequest = () => {
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Amount in Investor currency": (
|
||||
<Box display={'flex'} justifyContent={'end'} w={"100px"} isTruncated={true}>
|
||||
"Deposit Amount": (
|
||||
<Box
|
||||
display={"flex"}
|
||||
justifyContent={"end"}
|
||||
w={"130px"}
|
||||
isTruncated={true}
|
||||
>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{formatCurrency(removeTrailingZeros(item?.investorAmount))} <Badge ms={1} colorScheme="green">{item?.currencyCode}</Badge>
|
||||
{/* {formatCurrency(removeTrailingZeros(item?.investorAmount))} */}
|
||||
{parseFloat(item?.investorAmount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
<Badge ms={1} colorScheme="green">
|
||||
{item?.currencyCode}
|
||||
</Badge>
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
@@ -246,7 +293,7 @@ const DepositRequest = () => {
|
||||
onRejectOpen();
|
||||
}}
|
||||
py={1}
|
||||
// variant={"solid"}
|
||||
// variant={"solid"}
|
||||
>
|
||||
<CloseIcon fontSize={"10px"} />
|
||||
</Button>
|
||||
@@ -329,7 +376,7 @@ const DepositRequest = () => {
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
id={actionId}
|
||||
// firstField={firstField}
|
||||
// firstField={firstField}
|
||||
/>
|
||||
<DepositRequestReject
|
||||
isOpen={isRejectOpen}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
@@ -19,37 +22,50 @@ import {
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useGetDepositRequestByIdQuery, useUpdateDepositRequestMutation } from "../../../Services/deposit.request.service";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import {
|
||||
useGetDepositRequestByIdQuery,
|
||||
useGetDepositRequestQuery,
|
||||
useUpdateDepositRequestMutation,
|
||||
} from "../../../Services/deposit.request.service";
|
||||
import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
import CurrencyInput, { formatCurrency } from "../../../Components/CurrencyInput";
|
||||
|
||||
const FILE_TYPES = ["image/jpeg", "image/png", "image/gif"];
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
investorAmount: yup.string().required("Investor amount is required"),
|
||||
comment: yup.string().notRequired(),
|
||||
comment: yup.string().notRequired() .max(200, "Approve Comment cannot be more than 200 characters"),
|
||||
supporting_FileName: yup.mixed().required("File is required"),
|
||||
// .test("fileType", "Unsupported File Format", (value) => {
|
||||
// return value && FILE_TYPES.includes(value.type);
|
||||
// }),
|
||||
});
|
||||
|
||||
const DepositRequestApprove = ({ isOpen, onClose, firstField, id, data:requestData }) => {
|
||||
const toast = useToast()
|
||||
const DepositRequestApprove = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
firstField,
|
||||
id,
|
||||
data: requestData,
|
||||
}) => {
|
||||
const toast = useToast();
|
||||
const [file, setFile] = useState();
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
const [isBtnLoading, setIsBtnLoading] = useState(false);
|
||||
|
||||
const fileredData = requestData?.find((item)=> item?.id === id)
|
||||
console.log(fileredData);
|
||||
const [updateDepositRequest] = useUpdateDepositRequestMutation();
|
||||
const { data, isLoading } = useGetDepositRequestByIdQuery(id, {
|
||||
skip: !id,
|
||||
});
|
||||
|
||||
console.log(data?.data?.investorAmount);
|
||||
|
||||
|
||||
const [ updateDepositRequest ] = useUpdateDepositRequestMutation()
|
||||
|
||||
const {
|
||||
const {
|
||||
control,
|
||||
register,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
@@ -57,15 +73,17 @@ const DepositRequestApprove = ({ isOpen, onClose, firstField, id, data:requestDa
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
reset({
|
||||
investorAmount:fileredData?.investorAmount
|
||||
})
|
||||
|
||||
|
||||
}, [requestData, id])
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
setIsBtnLoading(true)
|
||||
if (data) {
|
||||
const investorAmount = parseFloat(data?.data?.investorAmount);
|
||||
reset({
|
||||
investorAmount: investorAmount,
|
||||
accountName: data?.data?.accountName,
|
||||
});
|
||||
}
|
||||
}, [id, data, reset]);
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
setIsBtnLoading(true);
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append("investorAmount", data.investorAmount);
|
||||
@@ -73,59 +91,38 @@ const DepositRequestApprove = ({ isOpen, onClose, firstField, id, data:requestDa
|
||||
const file = data.supporting_FileName["0"];
|
||||
formData.append("supporting_FileName", file);
|
||||
|
||||
try {
|
||||
const res = await updateDepositRequest({ id, data: formData });
|
||||
|
||||
try {
|
||||
const res = await updateDepositRequest({ id ,data: formData})
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.error?.data?.message} status={"error"} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false);
|
||||
} else if (res?.data?.statusCode === 200) {
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.data?.message} />,
|
||||
});
|
||||
setIsBtnLoading(false);
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.error?.data?.message} status={"error"} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}else if(res?.data?.statusCode === 200) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
|
||||
heandleOnClose();
|
||||
heandleOnClose();
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const selectedFile = event.target.files[0];
|
||||
setFile(selectedFile);
|
||||
const heandleOnClose = () => {
|
||||
reset();
|
||||
onClose();
|
||||
};
|
||||
|
||||
const { data, isLoading } =
|
||||
(id, {
|
||||
skip: !id,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
reset({
|
||||
investorAmount: data?.data?.investorAmount,
|
||||
});
|
||||
}
|
||||
}, [data, reset]);
|
||||
|
||||
const heandleOnClose = () =>{
|
||||
reset()
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onClose={heandleOnClose}
|
||||
initialFocusRef={firstField}
|
||||
>
|
||||
<ModalOverlay />
|
||||
|
||||
<ModalContent pb={4}>
|
||||
@@ -136,12 +133,16 @@ const DepositRequestApprove = ({ isOpen, onClose, firstField, id, data:requestDa
|
||||
) : (
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">Deposit Amount (SAR)</FormLabel>
|
||||
{/* <FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">
|
||||
Deposit Amount
|
||||
<Badge colorScheme="green">{data?.data?.currencyCode}</Badge>
|
||||
</FormLabel>
|
||||
<Input
|
||||
focusBorderColor="green.400"
|
||||
name="investorAmount"
|
||||
{...register("investorAmount")}
|
||||
value={formatCurrency(watch("investorAmount"))}
|
||||
fontSize="sm"
|
||||
type="number"
|
||||
size="sm"
|
||||
@@ -154,7 +155,24 @@ const DepositRequestApprove = ({ isOpen, onClose, firstField, id, data:requestDa
|
||||
{errors.investorAmount.message}
|
||||
</Text>
|
||||
)}
|
||||
</FormControl> */}
|
||||
|
||||
<FormControl mb={5} isRequired>
|
||||
<FormLabel fontSize={"sm"}>Deposit Amount</FormLabel>
|
||||
<Controller
|
||||
name="investorAmount"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<CurrencyInput {...field} textAlign={'right'} fontSize={"sm"} type="number" size={"sm"} />
|
||||
)}
|
||||
/>
|
||||
{errors.investorAmount && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.investorAmount.message}
|
||||
</Text>
|
||||
)}
|
||||
</FormControl>
|
||||
|
||||
<FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">Upload Supporting</FormLabel>
|
||||
<Input
|
||||
@@ -175,7 +193,7 @@ const DepositRequestApprove = ({ isOpen, onClose, firstField, id, data:requestDa
|
||||
)}
|
||||
</FormControl>
|
||||
<FormControl mb={4}>
|
||||
<FormLabel fontSize="sm">Comments</FormLabel>
|
||||
<FormLabel fontSize="sm">Comment</FormLabel>
|
||||
<Textarea
|
||||
rows={5}
|
||||
focusBorderColor="green.400"
|
||||
@@ -184,14 +202,19 @@ const DepositRequestApprove = ({ isOpen, onClose, firstField, id, data:requestDa
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="sm"
|
||||
placeholder={"Enter your comments...."}
|
||||
placeholder={"Enter your comment...."}
|
||||
resize={"none"}
|
||||
maxLength={200}
|
||||
/>
|
||||
{errors.comment && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.comment.message}
|
||||
</Text>
|
||||
)}
|
||||
<FormHelperText fontSize="xs" color="gray.500">
|
||||
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
|
||||
{watch("comment")?.length || 0} characters.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
@@ -24,7 +25,8 @@ import { useDepositRejectMutation } from "../../../Services/deposit.request.serv
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
comments: yup.string().required("Comment is required"),
|
||||
comments: yup.string().required("Comment is required")
|
||||
.max(200, "Approve Comment cannot be more than 200 characters"),
|
||||
});
|
||||
|
||||
const DepositRequestReject = ({ isOpen, onClose, firstField ,id}) => {
|
||||
@@ -35,6 +37,7 @@ const DepositRequestReject = ({ isOpen, onClose, firstField ,id}) => {
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
@@ -101,10 +104,10 @@ const DepositRequestReject = ({ isOpen, onClose, firstField ,id}) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
|
||||
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
|
||||
<ModalOverlay />
|
||||
<ModalContent pb={4}>
|
||||
<ModalHeader fontSize={"md"}>Reject</ModalHeader>
|
||||
<ModalHeader fontSize={"md"}>Investor Comment</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
{isLoading ? (
|
||||
<FullscreenLoaders height={"50vh"} />
|
||||
@@ -121,15 +124,20 @@ const DepositRequestReject = ({ isOpen, onClose, firstField ,id}) => {
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="md"
|
||||
placeholder={"Enter your comments...."}
|
||||
placeholder={"Enter your comment...."}
|
||||
rounded={"md"}
|
||||
resize={"none"}
|
||||
maxLength={200}
|
||||
/>
|
||||
{errors.comments && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.comments.message}
|
||||
</Text>
|
||||
)}
|
||||
<FormHelperText fontSize="xs" color="gray.500">
|
||||
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
|
||||
{watch("comments")?.length || 0} characters.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
|
||||
@@ -4,79 +4,108 @@ import {
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Switch,
|
||||
Text,
|
||||
Tooltip,
|
||||
useToast,
|
||||
useDisclosure,
|
||||
Link,
|
||||
Text,
|
||||
useBoolean,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { HiDotsVertical } from "react-icons/hi";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { debounce } from "../../Master/Sponser/AddSponser";
|
||||
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
|
||||
import Pagination from "../../../Components/Pagination";
|
||||
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
import NormalTable from "../../../Components/DataTable/NormalTable";
|
||||
import ConfirmModal from "./ConfirmModal";
|
||||
import RejectModal from "./RejectModal";
|
||||
import {
|
||||
useDepositRejectMutation,
|
||||
useGetDepositHistoryQuery,
|
||||
} from "../../../Services/deposit.request.service";
|
||||
import { ExternalLinkIcon } from "@chakra-ui/icons";
|
||||
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
|
||||
import Pagination from "../../../Components/Pagination";
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
|
||||
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
|
||||
import { useGetDepositHistoryQuery } from "../../../Services/deposit.request.service";
|
||||
import { debounce } from "../../Master/Sponser/AddSponser";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
|
||||
import { ExternalLinkIcon } from "@chakra-ui/icons";
|
||||
import InitiateReversalPopup from "../../../Components/Popups/InitiateReversalPopups";
|
||||
import { generateSerialNumber, isMaker } from "../../../Constants/Constants";
|
||||
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
|
||||
import { useCreateBankDepositReversalRequestMutation } from "../../../Services/bankdeposit.request.service";
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
}; // Simple date formatter
|
||||
|
||||
const DepositHistory = () => {
|
||||
const navigate = useNavigate();
|
||||
const toast = useToast();
|
||||
const { depositHistory, setDepositHistory, slideFromRight } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
// const {
|
||||
// isOpen: isConfirmOpen,
|
||||
// onOpen: onConfirmOpen,
|
||||
// onClose: onConfirmClose,
|
||||
// } = useDisclosure();
|
||||
// const {
|
||||
// isOpen: isRejectOpen,
|
||||
// onOpen: onRejectOpen,
|
||||
// onClose: onRejectClose,
|
||||
// } = useDisclosure();
|
||||
|
||||
const [createBankDepositReversalRequest] =
|
||||
useCreateBankDepositReversalRequestMutation();
|
||||
|
||||
// =========================== [Use State] =============================
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
const [reversalId, setReversalId] = useState();
|
||||
const {
|
||||
isOpen: isOpenInRev,
|
||||
onOpen: onOpenInRev,
|
||||
onClose: onCloseInRev,
|
||||
} = useDisclosure();
|
||||
const [isReversalLoading, setIsReversalLoading] = useBoolean();
|
||||
|
||||
// Debounce the search term to avoid making a request on every keystroke
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedSearchTerm(searchTerm);
|
||||
}, 500); // Adjust delay as needed
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [searchTerm]);
|
||||
|
||||
const {
|
||||
data,
|
||||
error,
|
||||
refetch,
|
||||
isLoading: depositHistoryLoading,
|
||||
} = useGetDepositHistoryQuery({ page: currentPage, size: pageSize });
|
||||
|
||||
} = useGetDepositHistoryQuery(
|
||||
{
|
||||
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
|
||||
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
|
||||
search: debouncedSearchTerm,
|
||||
},
|
||||
{
|
||||
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
|
||||
}
|
||||
);
|
||||
|
||||
// Use useEffect to refetch data when the component mounts
|
||||
useEffect(() => {
|
||||
refetch();
|
||||
}, [refetch]);
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
// "Sr.no",
|
||||
"Sr.no",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Country",
|
||||
"Phone Number",
|
||||
"Amount in Investor currency",
|
||||
"Deposit Amount",
|
||||
"Deposit Date",
|
||||
"Status",
|
||||
"Supporting's",
|
||||
isMaker() && "Reversal Action",
|
||||
];
|
||||
|
||||
const handleUpdateStatus = debounce((id) => {
|
||||
@@ -92,124 +121,126 @@ const DepositHistory = () => {
|
||||
});
|
||||
}, 300);
|
||||
|
||||
const filteredData = data?.data?.rows
|
||||
.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = [item.firstName, item.lastName, item.countryName]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
|
||||
// Filter by status (Uncomment and use if needed)
|
||||
// const status = item.status;
|
||||
// const statusLower = status ? "active" : "inactive";
|
||||
|
||||
// const statusMatches =
|
||||
// statusFilter === "all" ||
|
||||
// (statusFilter === "active" && status === true) ||
|
||||
// (statusFilter === "inactive" && status === false);
|
||||
|
||||
|
||||
const filteredData = data?.data?.rows
|
||||
.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = [item.firstName, item.lastName, item.countryName].filter(Boolean).join(' ');
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
|
||||
// Filter by status (Uncomment and use if needed)
|
||||
// const status = item.status;
|
||||
// const statusLower = status ? "active" : "inactive";
|
||||
|
||||
// const statusMatches =
|
||||
// statusFilter === "all" ||
|
||||
// (statusFilter === "active" && status === true) ||
|
||||
// (statusFilter === "inactive" && status === false);
|
||||
|
||||
return nameMatches;
|
||||
})
|
||||
.sort((b, a) => new Date(a.createdAt) - new Date(b.createdAt));
|
||||
|
||||
return nameMatches;
|
||||
})
|
||||
.sort((b, a) => new Date(a.createdAt) - new Date(b.createdAt));
|
||||
|
||||
// const handleView = (id) => {
|
||||
// setActionId(id);
|
||||
// onViewOpen();
|
||||
// };
|
||||
|
||||
const extractedArray =
|
||||
filteredData?.map((item, index) => ({
|
||||
"Sr.no": (
|
||||
<Text
|
||||
w={"30px"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{index + 1}
|
||||
const extractedArray = data?.data?.rows?.map((item, idx) => ({
|
||||
"Sr.no": (
|
||||
<Text
|
||||
w={"10px"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{generateSerialNumber(idx, currentPage, pageSize)}
|
||||
</Text>
|
||||
),
|
||||
"Client ID": (
|
||||
<Text
|
||||
w={"60px"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item?.clientReference_id}
|
||||
</Text>
|
||||
),
|
||||
"First Name": (
|
||||
<Box isTruncated={true} w={"60px"}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.firstName}
|
||||
</Text>
|
||||
),
|
||||
"Client ID": (
|
||||
<Text
|
||||
w={"60px"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item?.clientReference_id}
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"70px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.lastName}
|
||||
</Text>
|
||||
),
|
||||
"First Name": (
|
||||
<Box isTruncated={true} w={"60px"}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"70px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
Country: (
|
||||
<Box w={"80px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.countryName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"80px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.mobileNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Amount in Investor currency": (
|
||||
<Box w={"100px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{/* <Badge px={2} py={1}> */}
|
||||
{item?.investorAmount} <Badge ms={1} colorScheme="green">{item?.currencyCode}</Badge>
|
||||
{/* </Badge> */}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Deposit Date": (
|
||||
<Text
|
||||
w={"60px"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{formatDate(item?.createdAt)}
|
||||
</Box>
|
||||
),
|
||||
Country: (
|
||||
<Box w={"80px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.countryName}
|
||||
</Text>
|
||||
),
|
||||
Status: (
|
||||
<Box w={"70px"} isTruncated={true} cursor={"pointer"}>
|
||||
<Text
|
||||
|
||||
as={"span"}
|
||||
color={item.transactionStatus === "Approved" ? "green.500" : "red.500"}
|
||||
fontWeight={700}
|
||||
>
|
||||
{item.transactionStatus}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Supporting's": (
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"80px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.mobileNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Deposit Amount": (
|
||||
<Box isTruncated={true} display={"flex"} justifyContent={"end"}>
|
||||
<Text as={"span"} color={"teal.900"} textAlign={"right"}>
|
||||
{parseFloat(item?.investorAmount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
<Badge ms={1} colorScheme="green">
|
||||
{item?.currencyCode}
|
||||
</Badge>
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Deposit Date": (
|
||||
<Text
|
||||
w={"60px"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{formatDate(item?.createdAt)}
|
||||
</Text>
|
||||
),
|
||||
Status: (
|
||||
<Box w={"70px"} isTruncated={true} cursor={"pointer"}>
|
||||
<Text
|
||||
as={"span"}
|
||||
color={
|
||||
item.transactionStatus === "Approved" ? "green.500" : "red.500"
|
||||
}
|
||||
fontWeight={700}
|
||||
>
|
||||
{item.transactionStatus}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Supporting's":
|
||||
item.transactionStatus === "Approved" ? (
|
||||
<Text
|
||||
w={"60px"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
@@ -220,57 +251,116 @@ const filteredData = data?.data?.rows
|
||||
>
|
||||
{/* {item?.supporting_FileName} */}
|
||||
<Badge
|
||||
px={2}
|
||||
py={0.5}
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
colorScheme={"forestGreen"}
|
||||
>
|
||||
<Link
|
||||
href={"https://tanami.betadelivery.com/" + item?.supporting_FileName}
|
||||
isExternal
|
||||
px={2}
|
||||
py={0.5}
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
colorScheme={"forestGreen"}
|
||||
>
|
||||
<Box
|
||||
as="span"
|
||||
cursor={"pointer"}
|
||||
<Link
|
||||
href={import.meta.env.VITE_IMAGE_URL + item?.supporting_FileName}
|
||||
isExternal
|
||||
>
|
||||
View
|
||||
</Box>
|
||||
<ExternalLinkIcon />
|
||||
</Link>
|
||||
{/* <Link to="www.google.com" isExternal>
|
||||
<Box as="span" cursor={"pointer"}>
|
||||
View
|
||||
</Box>
|
||||
<ExternalLinkIcon />
|
||||
</Link>
|
||||
{/* <Link to="www.google.com" isExternal>
|
||||
<Box as="span">View</Box> <ExternalLinkIcon />
|
||||
</Link> */}
|
||||
</Badge>
|
||||
</Badge>
|
||||
</Text>
|
||||
) : (
|
||||
""
|
||||
),
|
||||
}))
|
||||
"Reversal Action": (
|
||||
<Box w={"120px"} isTruncated={true} cursor={"pointer"}>
|
||||
{item.transactionStatus === "Approved" ? (
|
||||
<Text
|
||||
as={"span"}
|
||||
color={!item.isReversal ? "green.500" : "#FFBB00"}
|
||||
fontWeight={700}
|
||||
>
|
||||
{!item.isReversal ? (
|
||||
<Button
|
||||
onClick={() => {
|
||||
onOpenInRev(); // Call the function
|
||||
setReversalId(item.id);
|
||||
}}
|
||||
colorScheme="teal"
|
||||
size="xs"
|
||||
variant="outline"
|
||||
>
|
||||
Initiate Reversal
|
||||
</Button>
|
||||
) : (
|
||||
"Under process"
|
||||
)}
|
||||
</Text>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const IOtype = investmentType.filter(
|
||||
(investmentType) => investmentType.id !== actionId
|
||||
);
|
||||
// const handleDelete = () => {
|
||||
// const IOtype = investmentType.filter(
|
||||
// (investmentType) => investmentType.id !== actionId
|
||||
// );
|
||||
|
||||
setTimeout(() => {
|
||||
setInvestmentType(IOtype);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
// setTimeout(() => {
|
||||
// setInvestmentType(IOtype);
|
||||
// setDeleteAlert(false);
|
||||
// setIsLoading(false);
|
||||
// }, 100);
|
||||
// setIsLoading(true);
|
||||
// };
|
||||
|
||||
const handleApproved = async (data) => {
|
||||
setIsReversalLoading.on(); // Start loading
|
||||
try {
|
||||
const { error, data: responseData } =
|
||||
await createBankDepositReversalRequest({
|
||||
id: reversalId,
|
||||
data,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error; // Explicitly handle the error
|
||||
}
|
||||
|
||||
// Success: Perform necessary actions
|
||||
refetch();
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={responseData?.message || "Action successful!"} />
|
||||
),
|
||||
});
|
||||
onCloseInRev();
|
||||
} catch (error) {
|
||||
// Handle errors
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox
|
||||
message={
|
||||
error?.data?.message || "Something went wrong. Please try again."
|
||||
}
|
||||
status="error"
|
||||
/>
|
||||
),
|
||||
});
|
||||
console.error("Error:", error);
|
||||
} finally {
|
||||
setIsReversalLoading.off(); // Ensure loading is toggled off
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
|
||||
{/* <ConfirmModal
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
/>
|
||||
<RejectModal
|
||||
isOpen={isRejectOpen}
|
||||
onClose={onRejectClose}
|
||||
/> */}
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
@@ -293,7 +383,7 @@ const filteredData = data?.data?.rows
|
||||
/>
|
||||
|
||||
<HStack display={"flex"} alignItems={"center"}>
|
||||
<Pagination
|
||||
<Pagination
|
||||
isLoading={depositHistoryLoading}
|
||||
pageSize={pageSize}
|
||||
setPageSize={setPageSize}
|
||||
@@ -317,12 +407,19 @@ const filteredData = data?.data?.rows
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
{/* <CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/> */}
|
||||
|
||||
<InitiateReversalPopup
|
||||
onClose={onCloseInRev}
|
||||
isOpen={isOpenInRev}
|
||||
handelApproved={handleApproved}
|
||||
isLoading={isReversalLoading}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
383
src/Pages/EmailNotification/EmailNotification.jsx
Normal file
@@ -0,0 +1,383 @@
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
HStack,
|
||||
Input,
|
||||
Select,
|
||||
Text,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
||||
import NormalTable from "../../Components/DataTable/NormalTable";
|
||||
import { useGetUnbanInvestorQuery } from "../../Services/ban.investor.service";
|
||||
import { formatDate, generateSerialNumber } from "../../Constants/Constants";
|
||||
import { TABLE_PAGINATION } from "../../Constants/Paginations";
|
||||
import ReactQuill from "react-quill";
|
||||
import "react-quill/dist/quill.snow.css"; // Importing the Quill snow theme
|
||||
import { useSendCustomEmailMutation } from "../../Services/contact.service";
|
||||
import { EmailIcon } from "@chakra-ui/icons";
|
||||
import ToastBox from "../../Components/ToastBox";
|
||||
|
||||
const EmailNotification = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [selectedRadio, setSelectedRadio] = useState([]);
|
||||
const [subject, setSubject] = useState("");
|
||||
const [value, setValue] = useState(""); // Quill content (body)
|
||||
const toast = useToast();
|
||||
const [sendCustomNotification] = useSendCustomEmailMutation();
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
const [country, setCountry] = useState("");
|
||||
const [kyc, setKyc] = useState("");
|
||||
|
||||
// ===========================[Table Setup]==============================
|
||||
const tableHeadRow = [
|
||||
"Sr N/O",
|
||||
"Date",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Country",
|
||||
"Phone Number",
|
||||
"E-mail ID",
|
||||
"KYC Status",
|
||||
];
|
||||
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
|
||||
// const {
|
||||
// data: investorDetails,
|
||||
// isLoading: investorDetailsLoading,
|
||||
// refetch,
|
||||
// } = useGetUnbanInvestorQuery({
|
||||
// page: currentPage, // Omit pagination for search
|
||||
// size: 10000, // Omit pagination for search
|
||||
// });
|
||||
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedSearchTerm(searchTerm.trim()); // Trim to remove leading/trailing spaces
|
||||
}, 300);
|
||||
return () => clearTimeout(handler);
|
||||
}, [searchTerm]);
|
||||
|
||||
const {
|
||||
data: investorDetails,
|
||||
isLoading: investorDetailsLoading,
|
||||
refetch,
|
||||
} = useGetUnbanInvestorQuery(
|
||||
{
|
||||
page: 1, // Omit pagination for search
|
||||
size: 10000, // Omit pagination for search
|
||||
// page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
|
||||
// size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
|
||||
search: debouncedSearchTerm,
|
||||
country_xid: country,
|
||||
KYCStatus: kyc,
|
||||
},
|
||||
{
|
||||
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
|
||||
}
|
||||
);
|
||||
|
||||
const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({
|
||||
id: item?.principal_xid,
|
||||
"Sr N/O": (
|
||||
<Text
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center fw-bold web-text-small"
|
||||
>
|
||||
{generateSerialNumber(idx, currentPage, pageSize)}
|
||||
</Text>
|
||||
),
|
||||
Date: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{formatDate(item?.date)}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Client ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.clientReference_id}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"First Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
Country: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.country}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.phoneNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"E-mail ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.emailAddress}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"KYC Status": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Badge
|
||||
fontWeight={"500"}
|
||||
textTransform={"none"}
|
||||
color={item?.KYCStatus === false ? "red" : "blue"}
|
||||
px={2}
|
||||
py={0.5}
|
||||
variant={"ghost"}
|
||||
>
|
||||
{item?.KYCStatus === true ? "Completed" : "Not Completed"}
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const modules = {
|
||||
toolbar: [
|
||||
// [{ header: "1" }, { header: "2" },
|
||||
// // { font: [] }
|
||||
// ],
|
||||
// [{ size: [] }],
|
||||
["bold", "italic", "underline", "strike", "blockquote"],
|
||||
[{ list: "ordered" }, { list: "bullet" }],
|
||||
["clean"],
|
||||
],
|
||||
};
|
||||
|
||||
// Main submission logic
|
||||
const handleSend = async (e) => {
|
||||
e.preventDefault(); // Prevent default form submission
|
||||
|
||||
if (!subject || !value) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox
|
||||
status={"error"}
|
||||
message={"Subject or email body cannot be empty"}
|
||||
/>
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedRadio.length === 0) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox
|
||||
status={"error"}
|
||||
message={"Please select at least one recipient"}
|
||||
/>
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
const emailPayload = {
|
||||
subject,
|
||||
body: value,
|
||||
principal_xid: selectedRadio,
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await sendCustomNotification(emailPayload);
|
||||
console.log(res);
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
} else if (res?.data) {
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.data?.message} />,
|
||||
});
|
||||
setIsLoading(false);
|
||||
setSubject("");
|
||||
setValue("");
|
||||
setSelectedRadio([]);
|
||||
} else {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={"Something went wrong"} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
as={"form"}
|
||||
{...OPACITY_ON_LOAD}
|
||||
overflowY={"scroll"}
|
||||
height={"100vh"}
|
||||
pb={14}
|
||||
pt={4}
|
||||
>
|
||||
<FormControl mb={0}>
|
||||
{/* <HStack
|
||||
py={4}
|
||||
pb={3}
|
||||
w={"100%"}
|
||||
justifyContent={"space-between"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Text as={"span"} fontSize={"sm"}>
|
||||
Customize your email
|
||||
</Text>
|
||||
</HStack> */}
|
||||
|
||||
<FormControl isRequired mb={3} p={1}>
|
||||
<FormLabel fontSize={"sm"}>Subject</FormLabel>
|
||||
<Input
|
||||
size={"md"}
|
||||
value={subject}
|
||||
onChange={(e) => setSubject(e.target.value)}
|
||||
focusBorderColor="forestGreen.300"
|
||||
rounded={0.5}
|
||||
type="text"
|
||||
/>
|
||||
{/* <FormHelperText>Entered subject will be reflected on emails subject body.</FormHelperText> */}
|
||||
</FormControl>
|
||||
|
||||
<FormControl minH={400} isRequired mb={3} p={1}>
|
||||
<FormLabel fontSize={"sm"}>Create Custom body</FormLabel>
|
||||
<ReactQuill
|
||||
theme="snow"
|
||||
style={{
|
||||
height: 300,
|
||||
}}
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
modules={modules}
|
||||
placeholder="Start typing here..."
|
||||
/>
|
||||
</FormControl>
|
||||
{/* <FormHelperText fontSize={"xs"}>
|
||||
We'll never share your email.
|
||||
</FormHelperText> */}
|
||||
</FormControl>
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
ps={1}
|
||||
pe={1}
|
||||
pb={4}
|
||||
pt={4}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
mt={1}
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
<HStack className="col" justifyContent={"end"}>
|
||||
<Select
|
||||
w={250}
|
||||
focusBorderColor="green.500"
|
||||
size={"sm"}
|
||||
fontSize={"xs"}
|
||||
cursor={"pointer"}
|
||||
onChange={(e) => setCountry(e.target.value)}
|
||||
value={country}
|
||||
>
|
||||
<option value="" defaultValue={""} disabled hidden>
|
||||
Country
|
||||
</option>
|
||||
<option value="">All</option>
|
||||
<option value="1">Bahrain</option>
|
||||
<option value="2">Kuwait</option>
|
||||
<option value="3">Oman</option>
|
||||
<option value="4">Qatar</option>
|
||||
<option value="5">Saudi arabia</option>
|
||||
<option value="6">United arab emirates</option>
|
||||
</Select>
|
||||
<Select
|
||||
w={250}
|
||||
focusBorderColor="green.500"
|
||||
size={"sm"}
|
||||
fontSize={"xs"}
|
||||
cursor={"pointer"}
|
||||
onChange={(e) => setKyc(e.target.value)}
|
||||
value={kyc}
|
||||
>
|
||||
<option value="" defaultValue={""} disabled hidden>
|
||||
KYC Status
|
||||
</option>
|
||||
<option value="">KYC Status</option>
|
||||
<option value="0">Not Completed</option>
|
||||
<option value="1">Completed</option>
|
||||
</Select>
|
||||
</HStack>
|
||||
</HStack>
|
||||
<Box overflow={"scroll"} h={"58vh"}>
|
||||
<NormalTable
|
||||
centered={true}
|
||||
emptyMessage={`We don't have any Sponsors`}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
setSelectedRadio={setSelectedRadio}
|
||||
selectedRadio={selectedRadio}
|
||||
showRadioButton={true}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<HStack justifyContent={"flex-end"} px={2}>
|
||||
<Button
|
||||
rightIcon={<EmailIcon />}
|
||||
rounded={"sm"}
|
||||
size={"sm"}
|
||||
colorScheme="forestGreen"
|
||||
type="submit"
|
||||
isLoading={isLoading}
|
||||
onClick={handleSend}
|
||||
>
|
||||
Send Email
|
||||
</Button>
|
||||
</HStack>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmailNotification;
|
||||
440
src/Pages/Fawateer/CreateRequest.jsx
Normal file
@@ -0,0 +1,440 @@
|
||||
import React, { useState } from "react";
|
||||
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputRightAddon,
|
||||
Textarea,
|
||||
useDisclosure,
|
||||
Image,
|
||||
Icon,
|
||||
VStack,
|
||||
Text,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import { FormControl, FormLabel, FormHelperText } from "@chakra-ui/react";
|
||||
import { DeleteIcon, Search2Icon } from "@chakra-ui/icons";
|
||||
import SelectInvestorModal from "./SelectInvestorModal";
|
||||
import { Controller, useForm } from "react-hook-form"; // Import useForm
|
||||
import { yupResolver } from "@hookform/resolvers/yup"; // Import resolver for Yup
|
||||
import * as Yup from "yup"; // Import Yup for validation
|
||||
import { motion } from "framer-motion"; // Import Framer Motion for animations
|
||||
import { bytesToMB } from "../../Constants/Constants";
|
||||
import { useCreateFawateerRequestMutation } from "../../Services/fawateer.maker.service";
|
||||
import ToastBox from "../../Components/ToastBox";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import CurrencyInput from "../../Components/CurrencyInput";
|
||||
|
||||
// Validation schema using Yup
|
||||
const validationSchema = Yup.object().shape({
|
||||
investorName: Yup.string().required("Investor name is required"),
|
||||
clientId: Yup.string().required("Client ID is required"),
|
||||
transaction_date: Yup.date()
|
||||
.required("Date is required")
|
||||
.transform((value, originalValue) => {
|
||||
return originalValue === "" ? null : value; // Convert empty strings to null
|
||||
})
|
||||
.typeError("Please enter a valid date")
|
||||
.max(new Date(), "Date cannot be in the future"),
|
||||
transaction_amount: Yup.number()
|
||||
.required("Transaction amount is required")
|
||||
.transform((value, originalValue) => (originalValue === "" ? null : value)) // Convert empty strings to null
|
||||
.typeError("Transaction amount must be a number") // Custom error message if it's not a number
|
||||
.positive("Transaction amount must be greater than zero"),
|
||||
spportFile_path: Yup.mixed().required("Support file is required"),
|
||||
makerComment: Yup.string() .max(200, "Approve Comment cannot be more than 50 characters"),
|
||||
});
|
||||
|
||||
const CreateRequest = () => {
|
||||
const toast = useToast();
|
||||
const navigate = useNavigate();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const [selectedInvestor, setSelectorInvestor] = useState({});
|
||||
const [filePreview, setFilePreview] = useState(null); // State for previewing the file
|
||||
const [fileType, setFileType] = useState(null); // State to store file type for conditional rendering
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [id, setId] = useState(null);
|
||||
|
||||
// Initialize useForm with the resolver for Yup validation
|
||||
const {
|
||||
control,
|
||||
register,
|
||||
watch,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
reset,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(validationSchema),
|
||||
});
|
||||
|
||||
const [creatFawaateerRequest] = useCreateFawateerRequestMutation();
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
console.log(data);
|
||||
setIsLoading(true);
|
||||
|
||||
// Convert data to FormData
|
||||
const formData = new FormData();
|
||||
|
||||
// Append each field from the data object to the FormData
|
||||
Object.keys(data).forEach((key) => {
|
||||
if (key === "spportFile_path" && data[key] instanceof FileList) {
|
||||
// Append the first file from FileList (assuming single file input)
|
||||
formData.append(key, data[key][0]); // Append the file
|
||||
} else {
|
||||
formData.append(key, data[key]); // Append other fields
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// Make the API call with formData
|
||||
const res = await creatFawaateerRequest({ data: formData, id });
|
||||
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
reset();
|
||||
return;
|
||||
} else if (res?.data) {
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.data?.message} />,
|
||||
});
|
||||
setIsLoading(false);
|
||||
navigate("/fawateer-history");
|
||||
return;
|
||||
} else {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={"Something went wrong"} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={"An error occurred"} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Handle file change and preview
|
||||
const handleFileChange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
console.log(file);
|
||||
setValue("spportFile_path", file); // Set the file value in the form
|
||||
setFileType(file); // Set the file type
|
||||
|
||||
if (file && file.type.startsWith("image/")) {
|
||||
// If the file is an image, generate a preview
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
setFilePreview(reader.result); // Set the image preview
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
setFilePreview(null); // Clear preview if the file is not an image
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
|
||||
<Box
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
flexWrap={"wrap"}
|
||||
alignItems={"center"}
|
||||
mt={5}
|
||||
px={4}
|
||||
as="form"
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
>
|
||||
{/* Investor Name Field */}
|
||||
<FormControl
|
||||
isRequired
|
||||
w={"49%"}
|
||||
mb={2}
|
||||
isInvalid={errors.investorName}
|
||||
>
|
||||
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
|
||||
Investor name
|
||||
</FormLabel>
|
||||
<InputGroup size="sm">
|
||||
<Input
|
||||
bg={"#F5F8F6"}
|
||||
focusBorderColor="forestGreen.300"
|
||||
size={"sm"}
|
||||
fontSize={"sm"}
|
||||
rounded={"sm"}
|
||||
type={"text"}
|
||||
readOnly={true}
|
||||
placeholder={"Investor name"}
|
||||
{...register("investorName")}
|
||||
_placeholder={{ fontSize: "sm" }}
|
||||
/>
|
||||
<InputRightAddon
|
||||
gap={2}
|
||||
color={"forestgreen.400"}
|
||||
onClick={onOpen}
|
||||
cursor={"pointer"}
|
||||
fontWeight={600}
|
||||
fontSize={"xs"}
|
||||
>
|
||||
<Search2Icon /> Search
|
||||
</InputRightAddon>
|
||||
</InputGroup>
|
||||
<FormHelperText
|
||||
fontSize={"xs"}
|
||||
fontWeight={500}
|
||||
style={{ color: "red" }}
|
||||
>
|
||||
{errors.investorName?.message}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* Client ID Field */}
|
||||
<FormControl isRequired w={"49%"} mb={2} isInvalid={errors.clientId}>
|
||||
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
|
||||
Client Id
|
||||
</FormLabel>
|
||||
<Input
|
||||
bg={"#F5F8F6"}
|
||||
focusBorderColor="forestGreen.300"
|
||||
size={"sm"}
|
||||
fontSize={"sm"}
|
||||
rounded={"sm"}
|
||||
type={"text"}
|
||||
readOnly={true}
|
||||
placeholder={"Client ID"}
|
||||
{...register("clientId")}
|
||||
/>
|
||||
<FormHelperText
|
||||
fontSize={"xs"}
|
||||
fontWeight={500}
|
||||
style={{ color: "red" }}
|
||||
>
|
||||
{errors.clientId?.message}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* Date Field */}
|
||||
<FormControl isRequired w={"49%"} mb={2} isInvalid={errors.date}>
|
||||
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
|
||||
Date
|
||||
</FormLabel>
|
||||
<Input
|
||||
bg={"#F5F8F6"}
|
||||
focusBorderColor="forestGreen.300"
|
||||
size={"sm"}
|
||||
fontSize={"sm"}
|
||||
rounded={"sm"}
|
||||
type={"date"}
|
||||
max={new Date().toLocaleDateString("en-CA")} // Ensures max is in local timezone
|
||||
{...register("transaction_date", {
|
||||
setValueAs: (value) => {
|
||||
// Convert date string to local timezone Date object
|
||||
return value ? new Date(value) : undefined;
|
||||
},
|
||||
})}
|
||||
/>
|
||||
<FormHelperText
|
||||
fontSize={"xs"}
|
||||
fontWeight={500}
|
||||
style={{ color: "red" }}
|
||||
>
|
||||
{errors.transaction_date?.message}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* Amount Field */}
|
||||
<FormControl isRequired w={"49%"} mb={2} isInvalid={errors.amount}>
|
||||
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
|
||||
Amount (BHD)
|
||||
</FormLabel>
|
||||
<Controller
|
||||
name="transaction_amount"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<CurrencyInput
|
||||
bg={"#F5F8F6"}
|
||||
{...field}
|
||||
textAlign={"right"}
|
||||
fontSize={"sm"}
|
||||
type="number"
|
||||
size={"sm"}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<FormHelperText
|
||||
fontSize={"xs"}
|
||||
fontWeight={500}
|
||||
style={{ color: "red" }}
|
||||
>
|
||||
{errors.transaction_amount?.message}
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* Support File Field with Preview */}
|
||||
<FormControl
|
||||
isRequired
|
||||
w={"49%"}
|
||||
mb={2}
|
||||
isInvalid={errors.spportFile_path}
|
||||
>
|
||||
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
|
||||
Support file
|
||||
</FormLabel>
|
||||
<Input
|
||||
bg={"#F5F8F6"}
|
||||
focusBorderColor="forestGreen.300"
|
||||
size={"sm"}
|
||||
fontSize={"sm"}
|
||||
rounded={"sm"}
|
||||
type={"file"}
|
||||
className="form-control"
|
||||
name="spportFile_path"
|
||||
placeholder={"Support file"}
|
||||
{...register("spportFile_path")}
|
||||
// onChange={handleFileChange}
|
||||
/>
|
||||
<FormHelperText
|
||||
fontSize={"xs"}
|
||||
fontWeight={500}
|
||||
style={{ color: "red" }}
|
||||
>
|
||||
{errors.spportFile_path?.message}
|
||||
</FormHelperText>
|
||||
|
||||
{/* Animated Preview */}
|
||||
{filePreview && fileType?.type.startsWith("image/") && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
style={{ marginTop: "10px" }}
|
||||
>
|
||||
<Box
|
||||
position={"relative"}
|
||||
display={"flex"}
|
||||
alignContent={"flex-end"}
|
||||
gap={3}
|
||||
mt={2}
|
||||
>
|
||||
<Image
|
||||
src={filePreview}
|
||||
alt="File preview"
|
||||
maxW={"150px"}
|
||||
borderRadius="md"
|
||||
boxShadow="md"
|
||||
/>
|
||||
<Icon
|
||||
onClick={() => setFilePreview(null)}
|
||||
className="link"
|
||||
rounded={"md"}
|
||||
color={"red.700"}
|
||||
cursor={"pointer"}
|
||||
p={1.5}
|
||||
position={"absolute"}
|
||||
top={0}
|
||||
right={0}
|
||||
as={DeleteIcon}
|
||||
boxSize={7}
|
||||
/>
|
||||
<VStack justifyItems={"flex-end"} alignItems={"flex-start"}>
|
||||
<Text as={"span"} color={"gray.600"} fontSize={"xs"}>
|
||||
File Name:{" "}
|
||||
<Text as={"span"} color={"GrayText"}>
|
||||
{" "}
|
||||
{fileType?.name}
|
||||
</Text>
|
||||
</Text>
|
||||
<Text as={"span"} color={"gray.600"} fontSize={"xs"}>
|
||||
File Size:{" "}
|
||||
<Text as={"span"} color={"GrayText"}>
|
||||
{" "}
|
||||
{bytesToMB(fileType?.size)} Mb
|
||||
</Text>
|
||||
</Text>
|
||||
<Text as={"span"} color={"gray.600"} fontSize={"xs"}>
|
||||
File Type:{" "}
|
||||
<Text as={"span"} color={"GrayText"}>
|
||||
{" "}
|
||||
{fileType?.type}
|
||||
</Text>
|
||||
</Text>
|
||||
</VStack>
|
||||
</Box>
|
||||
</motion.div>
|
||||
)}
|
||||
</FormControl>
|
||||
|
||||
{/* Description Field */}
|
||||
<FormControl w={"100%"} mb={2} isInvalid={errors.makerComment}>
|
||||
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
|
||||
Description
|
||||
</FormLabel>
|
||||
<Textarea
|
||||
bg={"#F5F8F6"}
|
||||
focusBorderColor="forestGreen.300"
|
||||
size={"sm"}
|
||||
fontSize={"sm"}
|
||||
rounded={"sm"}
|
||||
placeholder={"Description"}
|
||||
{...register("makerComment")}
|
||||
maxLength={200}
|
||||
/>
|
||||
<FormHelperText
|
||||
fontSize={"xs"}
|
||||
fontWeight={500}
|
||||
style={{ color: "red" }}
|
||||
>
|
||||
{errors.makerComment?.message}
|
||||
</FormHelperText>
|
||||
<FormHelperText fontSize="xs" color="gray.500">
|
||||
<Box as="span" me={1}>
|
||||
Maximum length should be 200 characters. You have entered
|
||||
</Box>
|
||||
{watch("makerComment")?.length || 0} characters.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
{/* Submit Button */}
|
||||
<HStack mt={2} w={"100%"} justifyContent={"flex-end"}>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
type="submit"
|
||||
isLoading={isLoading}
|
||||
>
|
||||
Create request
|
||||
</Button>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<SelectInvestorModal
|
||||
setId={setId}
|
||||
setValue={setValue}
|
||||
onClose={onClose}
|
||||
isOpen={isOpen}
|
||||
onOpen={onOpen}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateRequest;
|
||||
@@ -1,57 +1,74 @@
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Portal,
|
||||
Select,
|
||||
Switch,
|
||||
Tag,
|
||||
Text,
|
||||
Tooltip,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
useToast
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useState,useRef } from "react";
|
||||
import { HiDotsVertical } from "react-icons/hi";
|
||||
import { Link, Link as RouterLink,useNavigate } from "react-router-dom";
|
||||
import {
|
||||
AddIcon,
|
||||
DeleteIcon,
|
||||
EditIcon,
|
||||
EmailIcon,
|
||||
ViewIcon,
|
||||
} from "@chakra-ui/icons";
|
||||
import GlobalStateContext from "../../Contexts/GlobalStateContext";
|
||||
import { debounce } from "../Master/Sponser/AddSponser";
|
||||
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
||||
import DataTable from "../../Components/DataTable/DataTable";
|
||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import CustomAlertDialog from "../../Components/CustomAlertDialog";
|
||||
import NormalTable from "../../Components/DataTable/NormalTable";
|
||||
import Pagination from "../../Components/Pagination";
|
||||
import ToastBox from "../../Components/ToastBox";
|
||||
import GlobalStateContext from "../../Contexts/GlobalStateContext";
|
||||
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
||||
import { debounce } from "../Master/Sponser/AddSponser";
|
||||
// import InvestmentDetailsEdit from "./InvestmentDetailsEdit";
|
||||
import { generateSerialNumber } from "../../Constants/Constants";
|
||||
import { TABLE_PAGINATION } from "../../Constants/Paginations";
|
||||
import { useGetInvestorsQuery } from "../../Services/investor.details.service";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
|
||||
|
||||
const BankInvestor = () => {
|
||||
const FawateerRequest = () => {
|
||||
const navigate = useNavigate();
|
||||
const toast = useToast();
|
||||
const thirdField = useRef();
|
||||
const { bankInvestor, setBankInvestor, slideFromRight } =
|
||||
const { InvestorDetails, setInvestorDetails, slideFromRight } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
const {isOpen: isEditOpen,onOpen: onEditOpen,onClose: onEditClose,} = useDisclosure();
|
||||
const btnRef = React.useRef()
|
||||
const {
|
||||
isOpen: isEditOpen,
|
||||
onOpen: onEditOpen,
|
||||
onClose: onEditClose,
|
||||
} = useDisclosure();
|
||||
const btnRef = React.useRef();
|
||||
|
||||
// =========================== [Use State] =============================
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
|
||||
// Debounce the search term to avoid making a request on every keystroke
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedSearchTerm(searchTerm);
|
||||
}, 500); // Adjust delay as needed
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [searchTerm]);
|
||||
|
||||
const {
|
||||
data: investorDetails,
|
||||
isLoading: investorDetailsLoading,
|
||||
error,
|
||||
} = useGetInvestorsQuery(
|
||||
{
|
||||
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
|
||||
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
|
||||
search: debouncedSearchTerm,
|
||||
},
|
||||
{
|
||||
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
@@ -65,21 +82,22 @@ const BankInvestor = () => {
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr N/O",
|
||||
"Sr No",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Country",
|
||||
"Phone Number",
|
||||
"E-mail ID",
|
||||
"KYC Status",
|
||||
"Action",
|
||||
// "Type",
|
||||
// "KYC Status",
|
||||
"Approval Status",
|
||||
];
|
||||
|
||||
const handleUpdateStatus = debounce((id) => {
|
||||
setBankInvestor((prevData) =>
|
||||
prevData.map((bankInvestor) =>
|
||||
bankInvestor.id === id ? { ...bankInvestor } : bankInvestor
|
||||
setInvestorDetails((prevData) =>
|
||||
prevData.map((InvestorDetails) =>
|
||||
InvestorDetails.id === id ? { ...InvestorDetails } : InvestorDetails
|
||||
)
|
||||
);
|
||||
toast({
|
||||
@@ -88,9 +106,17 @@ const BankInvestor = () => {
|
||||
}, 300);
|
||||
|
||||
// ====================================================[Table Filter]================================================================
|
||||
const filteredData = bankInvestor?.filter((item) => {
|
||||
const filteredData = investorDetails?.data?.rows?.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item.firstName;
|
||||
const name = [
|
||||
item?.principal?.firstName,
|
||||
item?.principal?.lastName,
|
||||
item?.country?.countryName,
|
||||
item?.principal?.mobileNumber,
|
||||
item?.principal?.emailAddress,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name?.toLowerCase().includes(searchLower);
|
||||
|
||||
@@ -106,74 +132,102 @@ const BankInvestor = () => {
|
||||
return nameMatches;
|
||||
});
|
||||
|
||||
const extractedArray = filteredData?.map((item) => ({
|
||||
const customHeaders = [
|
||||
{ label: "ID", key: "id" },
|
||||
{ label: "Client ID", key: "clientReference_id" },
|
||||
{ label: "First Name", key: "principal.firstName" }, // Nested property
|
||||
{ label: "Last Name", key: "principal.lastName" }, // Nested property
|
||||
{ label: "Country", key: "country.countryName" }, // Nested property
|
||||
{ label: "Phone Number", key: "principal.mobileNumber" }, // Nested property
|
||||
{ label: "E-mail ID", key: "principal.emailAddress" }, // Nested property
|
||||
{ label: "Type", key: "investor_type.investorTypeName" }, // Nested property
|
||||
{ label: "Status", key: "ioStatus" }, // Simple property
|
||||
{ label: "KYC Status", key: "KYCStatus" }, // Simple property
|
||||
];
|
||||
|
||||
const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({
|
||||
id: item?.id,
|
||||
"Sr N/O": (
|
||||
"Sr No": (
|
||||
<Text
|
||||
w={"24px"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center fw-bold web-text-small"
|
||||
>
|
||||
{item.id}
|
||||
{/* {item.id} */}
|
||||
{generateSerialNumber(idx, currentPage, pageSize)}
|
||||
</Text>
|
||||
),
|
||||
"Client ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.clientId}
|
||||
{item.clientReference_id}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"First Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.firstName}
|
||||
{item?.principal?.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Box w={"70px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.lastName}
|
||||
{item?.principal?.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
Country: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.country}
|
||||
{item?.country?.countryName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.phoneNumber}
|
||||
{item?.principal?.mobileNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"E-mail ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.mailId}
|
||||
{item?.principal?.emailAddress}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"KYC Status": (
|
||||
Type: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Badge fontWeight={'500'} textTransform={'none'} color={item.status === "Completed" ? "blue" : "red"} px={2} py={0.5}>
|
||||
{item.status}
|
||||
</Badge>
|
||||
<Text as={"span"}>
|
||||
<Badge
|
||||
color={"forestGreen.500"}
|
||||
variant={"ghost"}
|
||||
fontWeight={"700"}
|
||||
px={2}
|
||||
py={0.5}
|
||||
>
|
||||
{item?.investor_type?.investorTypeName}
|
||||
</Badge>
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
Action: (
|
||||
<Switch
|
||||
size={"sm"}
|
||||
colorScheme="forestGreen"
|
||||
onChange={() => handleUpdateStatus(item.id)}
|
||||
isChecked={item.status}
|
||||
/>
|
||||
"Approval Status": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Badge
|
||||
fontWeight={"700"}
|
||||
textTransform={"none"}
|
||||
colorScheme={item.ioStatus ? "red" : "purple"}
|
||||
px={2}
|
||||
py={0.5}
|
||||
>
|
||||
Approved
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
@@ -195,6 +249,8 @@ const BankInvestor = () => {
|
||||
onEditOpen();
|
||||
};
|
||||
|
||||
console.log(investorDetails?.data?.totalItems);
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
|
||||
<Box bg="white.500">
|
||||
@@ -208,7 +264,7 @@ const BankInvestor = () => {
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
mt={1}
|
||||
mt={1}
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
@@ -218,17 +274,38 @@ const BankInvestor = () => {
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<HStack display={"flex"} alignItems={"center"}>
|
||||
<Pagination totalItems={10} />
|
||||
<Pagination
|
||||
isLoading={investorDetailsLoading}
|
||||
pageSize={pageSize}
|
||||
setPageSize={setPageSize}
|
||||
currentPage={currentPage}
|
||||
setCurrentPage={setCurrentPage}
|
||||
totalItems={investorDetails?.data?.totalItems}
|
||||
/>
|
||||
|
||||
{/*
|
||||
<Button
|
||||
leftIcon={<AddIcon />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
onClick={()=> navigate('create-request')}
|
||||
>
|
||||
Create request
|
||||
</Button> */}
|
||||
</HStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<DataTable
|
||||
<NormalTable
|
||||
// centered={true}
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
isLoading={investorDetailsLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
@@ -246,4 +323,4 @@ const BankInvestor = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default BankInvestor;
|
||||
export default FawateerRequest;
|
||||
247
src/Pages/Fawateer/SelectInvestorModal.jsx
Normal file
@@ -0,0 +1,247 @@
|
||||
import {
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalFooter,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
Button,
|
||||
Text,
|
||||
Box,
|
||||
Badge,
|
||||
Select,
|
||||
HStack,
|
||||
Input,
|
||||
useToast,
|
||||
} from '@chakra-ui/react'
|
||||
import NormalTable from '../../Components/DataTable/NormalTable'
|
||||
import { generateSerialNumber } from '../../Constants/Constants';
|
||||
import { useGetInvestorsQuery } from '../../Services/investor.details.service';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { TABLE_PAGINATION } from '../../Constants/Paginations';
|
||||
import Pagination from '../../Components/Pagination';
|
||||
import { AddIcon } from '@chakra-ui/icons';
|
||||
import { useGetFawateerInvestorsQuery } from '../../Services/fawateer.request.service';
|
||||
import ToastBox from '../../Components/ToastBox';
|
||||
|
||||
const SelectInvestorModal = ({ isOpen, setValue, onClose, setId}) => {
|
||||
const toast = useToast()
|
||||
// =========================== [Use State] =============================
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
|
||||
// Debounce the search term to avoid making a request on every keystroke
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedSearchTerm(searchTerm);
|
||||
}, 500); // Adjust delay as needed
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [searchTerm]);
|
||||
|
||||
const {
|
||||
data: investorDetails,
|
||||
isLoading: investorDetailsLoading,
|
||||
error,
|
||||
} = useGetFawateerInvestorsQuery({
|
||||
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
|
||||
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
|
||||
search: debouncedSearchTerm,
|
||||
},
|
||||
{
|
||||
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
|
||||
}
|
||||
);
|
||||
|
||||
const [ selectedRadio, setSelectedRadio] = useState([])
|
||||
const [ selectedInvestor, setSelectorInvestor] = useState(null)
|
||||
|
||||
const handleCheckboxChange = (id) => {
|
||||
|
||||
setSelectedRadio([id]);
|
||||
const investor = investorDetails?.data?.rows?.find((item)=> item?.id === id)
|
||||
setSelectorInvestor(investor)
|
||||
setId(investor?.principal_xid)
|
||||
// setValue("investorName",`${selectedInvestor?.firstName} ${selectedInvestor?.lastName}`)
|
||||
// setValue("clientId",selectedInvestor?.clientReference_id)
|
||||
return
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
if(selectedRadio?.length === 0){
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={'info'} message={"Please Select Investor"} />
|
||||
),
|
||||
});
|
||||
return
|
||||
}
|
||||
setValue("investorName",`${selectedInvestor?.firstName} ${selectedInvestor?.lastName}`)
|
||||
setValue("clientId",selectedInvestor?.clientReference_id)
|
||||
return onClose()
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
|
||||
"Sr No",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
// "Country",
|
||||
"Phone Number",
|
||||
"E-mail ID",
|
||||
// "Type",
|
||||
// "KYC Status",
|
||||
// "Approval Status",
|
||||
];
|
||||
|
||||
|
||||
const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({
|
||||
id: item?.id,
|
||||
"Sr No": (
|
||||
<Text
|
||||
w={'24px'}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center fw-bold web-text-small"
|
||||
>
|
||||
{/* {item.id} */}
|
||||
{generateSerialNumber(idx,currentPage, pageSize )}
|
||||
|
||||
</Text>
|
||||
),
|
||||
"Client ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.clientReference_id}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"First Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"70px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.phoneNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"E-mail ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.emailAddress}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Type": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} >
|
||||
<Badge color={"forestGreen.500"} variant={'ghost'} fontWeight={"700"} px={2} py={0.5}>
|
||||
{item?.investor_type?.investorTypeName}
|
||||
</Badge>
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Approval Status": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Badge
|
||||
fontWeight={"700"}
|
||||
textTransform={"none"}
|
||||
colorScheme={item.ioStatus ? "red" : "purple"}
|
||||
px={2}
|
||||
py={0.5}
|
||||
>
|
||||
Approved
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
|
||||
const handleClose = () => {
|
||||
setSelectorInvestor(null)
|
||||
handleClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isCentered
|
||||
onClose={onClose}
|
||||
isOpen={isOpen}
|
||||
motionPreset='scale'
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent maxW={1200}>
|
||||
<ModalHeader fontSize={'md'}>View Investor's</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
{/* <Lorem count={2} /> */}
|
||||
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
ps={1}
|
||||
pe={1}
|
||||
pb={4}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
mt={1}
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
<Button leftIcon={<AddIcon/>} fontSize={'xs'} colorScheme='forestGreen' size={'sm'} rounded={'sm'} onClick={handleAdd}>
|
||||
Select Investor
|
||||
</Button>
|
||||
|
||||
</HStack>
|
||||
|
||||
|
||||
{investorDetailsLoading?"Loaading":<NormalTable
|
||||
// centered={true}
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={investorDetailsLoading}
|
||||
|
||||
|
||||
|
||||
handleCheckboxChange={handleCheckboxChange}
|
||||
setSelectedRadio={setSelectedRadio}
|
||||
selectedRadio={selectedRadio}
|
||||
showRadioButton={true}
|
||||
radio={true}
|
||||
/>}
|
||||
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default SelectInvestorModal
|
||||
@@ -0,0 +1,360 @@
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Link,
|
||||
Text,
|
||||
Tooltip,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { CheckIcon, CloseIcon, ExternalLinkIcon } from "@chakra-ui/icons";
|
||||
import Pagination from "../../../Components/Pagination";
|
||||
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
|
||||
import DrawalRequestReject from "../../WithDrawal/DrawalRequest/DrawalRequestReject";
|
||||
import NormalTable from "../../../Components/DataTable/NormalTable";
|
||||
import DrawalRequestApprove from "../../WithDrawal/DrawalRequest/DrawalRequestApprove";
|
||||
import { generateSerialNumber } from "../../../Constants/Constants";
|
||||
import {
|
||||
useGetApproveHistoryQuery,
|
||||
useGetFawateerRequestQuery,
|
||||
} from "../../../Services/fawateer.request.service";
|
||||
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
|
||||
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
|
||||
|
||||
const ApproveHistory = () => {
|
||||
const toast = useToast();
|
||||
const { slideFromRight, approveHistory, setApproveHistory } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
const [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();
|
||||
|
||||
// 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(
|
||||
{
|
||||
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
|
||||
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
|
||||
searchTerm: debouncedSearchTerm,
|
||||
},
|
||||
{
|
||||
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
|
||||
}
|
||||
);
|
||||
|
||||
console.log(data?.data?.rows);
|
||||
|
||||
// Use useEffect to refetch data when the component mounts
|
||||
useEffect(() => {
|
||||
refetch();
|
||||
}, [refetch]);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
|
||||
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)
|
||||
const name = item.firstName;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
|
||||
// Filter by status
|
||||
// const status = item.status;
|
||||
// const statusLower = status ? "active" : "inactive";
|
||||
|
||||
// const statusMatches =
|
||||
// statusFilter === "all" ||
|
||||
// (statusFilter === "active" && status === true) ||
|
||||
// (statusFilter === "inactive" && status === false);
|
||||
|
||||
return nameMatches;
|
||||
});
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr.no",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"E-mail ID",
|
||||
"Phone Number",
|
||||
"Deposit Date",
|
||||
"Deposit Amount (BHD)",
|
||||
"Support Image",
|
||||
"Status",
|
||||
];
|
||||
|
||||
const extractedArray = data?.data?.rows?.map((item, idx) => ({
|
||||
// id: item?.id,
|
||||
"Sr.no": (
|
||||
<Text
|
||||
w={"auto"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{generateSerialNumber(idx, currentPage, pageSize)}
|
||||
</Text>
|
||||
),
|
||||
"Client ID": (
|
||||
<Text
|
||||
w={"60px"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.clientReference_id}
|
||||
</Text>
|
||||
),
|
||||
"First Name": (
|
||||
<Box isTruncated={true} w={"80px"}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"50px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"E-mail ID": (
|
||||
<Box isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.emailAddress}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"100px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.mobileNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Deposit Date": (
|
||||
<Box w={"100px"} isTruncated={true} display={"flex"}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{formatDate(item?.transaction_date)}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Deposit Amount (BHD)": (
|
||||
<Box w={"130px"} isTruncated={true} display={"flex"}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{/* {item.investorAmount} */}
|
||||
{parseFloat(item?.transaction_amount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
{/* <Badge ms={1} colorScheme="green">{item?.transaction_amount}</Badge> */}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Support Image": (
|
||||
<Text
|
||||
color={"green.500"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item?.spportFile_path && (
|
||||
<Badge
|
||||
px={2}
|
||||
py={0.5}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
colorScheme={"forestGreen"}
|
||||
>
|
||||
<Link
|
||||
href={import.meta.env.VITE_IMAGE_URL + item?.spportFile_path}
|
||||
isExternal
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Box me={"1px"} as="span" cursor={"pointer"}>
|
||||
View
|
||||
</Box>
|
||||
<ExternalLinkIcon />
|
||||
</Link>
|
||||
</Badge>
|
||||
)}
|
||||
</Text>
|
||||
),
|
||||
Status: (
|
||||
<Box isTruncated={true} display={"flex"}>
|
||||
<Badge
|
||||
my={1}
|
||||
fontWeight={500}
|
||||
px={2}
|
||||
py={"2px"}
|
||||
rounded={4}
|
||||
colorScheme={
|
||||
item?.transactionStatus === "Approved"
|
||||
? "green"
|
||||
: item?.transactionStatus === "Pending"
|
||||
? "yellow"
|
||||
: item?.transactionStatus === "Reject"
|
||||
? "red"
|
||||
: "gray" // default border color if status doesn't match any condition
|
||||
}
|
||||
>
|
||||
{item.transactionStatus}
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedSponsors = sponser.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setSponser(updatedSponsors);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
ps={1}
|
||||
pe={1}
|
||||
pb={4}
|
||||
pt={4}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<Pagination
|
||||
isLoading={drawalRequestLoading}
|
||||
pageSize={pageSize}
|
||||
setPageSize={setPageSize}
|
||||
currentPage={currentPage}
|
||||
setCurrentPage={setCurrentPage}
|
||||
totalItems={data?.data?.totalItems}
|
||||
/>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
isLoading={drawalRequestLoading}
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
// totalPages={10}
|
||||
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<DrawalRequestApprove
|
||||
// data={data?.data?.rows}
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
id={actionId}
|
||||
// firstField={firstField}
|
||||
/>
|
||||
<DrawalRequestReject
|
||||
isOpen={isRejectOpen}
|
||||
onClose={onRejectClose}
|
||||
id={actionId}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApproveHistory;
|
||||
442
src/Pages/FawateerChecker/ApproveHistory/ApproveHistoryMaker.jsx
Normal file
@@ -0,0 +1,442 @@
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Link,
|
||||
Text,
|
||||
Tooltip,
|
||||
useBoolean,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { CheckIcon, CloseIcon, ExternalLinkIcon } from "@chakra-ui/icons";
|
||||
import Pagination from "../../../Components/Pagination";
|
||||
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
|
||||
import DrawalRequestReject from "../../WithDrawal/DrawalRequest/DrawalRequestReject";
|
||||
import NormalTable from "../../../Components/DataTable/NormalTable";
|
||||
import DrawalRequestApprove from "../../WithDrawal/DrawalRequest/DrawalRequestApprove";
|
||||
import { generateSerialNumber, isMaker } from "../../../Constants/Constants";
|
||||
import {
|
||||
useGetApproveHistoryQuery,
|
||||
useGetFawateerForMakerRequestQuery,
|
||||
useGetFawateerRequestQuery,
|
||||
} from "../../../Services/fawateer.request.service";
|
||||
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
|
||||
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
|
||||
import InitiateReversalPopup from "../../../Components/Popups/InitiateReversalPopups";
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
import { useCreateFawateerReversalRequestMutation } from "../../../Services/reversal.fawateer.deposit.service";
|
||||
|
||||
const ApproveHistoryMaker = () => {
|
||||
const toast = useToast();
|
||||
const { slideFromRight, approveHistory, setApproveHistory } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
|
||||
const [createFawateerReversalRequest] =
|
||||
useCreateFawateerReversalRequestMutation();
|
||||
|
||||
const [reversalId, setReversalId] = useState();
|
||||
const {
|
||||
isOpen: isOpenInRev,
|
||||
onOpen: onOpenInRev,
|
||||
onClose: onCloseInRev,
|
||||
} = useDisclosure();
|
||||
const [isReversalLoading, setIsReversalLoading] = useBoolean();
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
onOpen: onConfirmOpen,
|
||||
onClose: onConfirmClose,
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isRejectOpen,
|
||||
onOpen: onRejectOpen,
|
||||
onClose: onRejectClose,
|
||||
} = useDisclosure();
|
||||
|
||||
const {
|
||||
data,
|
||||
isLoading: drawalRequestLoading,
|
||||
error,
|
||||
refetch,
|
||||
} = useGetFawateerForMakerRequestQuery(
|
||||
{
|
||||
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
|
||||
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
|
||||
searchTerm: debouncedSearchTerm,
|
||||
},
|
||||
{
|
||||
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
|
||||
}
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedSearchTerm(searchTerm);
|
||||
}, 500); // Adjust delay as needed
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [searchTerm]);
|
||||
|
||||
// Use useEffect to refetch data when the component mounts
|
||||
useEffect(() => {
|
||||
refetch();
|
||||
}, [refetch]);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
// ====================================================[Table Filter]================================================================
|
||||
const filteredData = data?.data?.rows?.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item.firstName;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
|
||||
// Filter by status
|
||||
// const status = item.status;
|
||||
// const statusLower = status ? "active" : "inactive";
|
||||
|
||||
// const statusMatches =
|
||||
// statusFilter === "all" ||
|
||||
// (statusFilter === "active" && status === true) ||
|
||||
// (statusFilter === "inactive" && status === false);
|
||||
|
||||
return nameMatches;
|
||||
});
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr.no",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"E-mail ID",
|
||||
"Phone Number",
|
||||
"Deposit Date",
|
||||
"Deposit Amount (BHD)",
|
||||
"Support Image",
|
||||
"Status",
|
||||
isMaker() && "Reversal Action",
|
||||
];
|
||||
|
||||
const extractedArray = data?.data?.rows?.map((item, idx) => ({
|
||||
// id: item?.id,
|
||||
"Sr.no": (
|
||||
<Text
|
||||
w={"auto"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{generateSerialNumber(idx, currentPage, pageSize)}
|
||||
</Text>
|
||||
),
|
||||
"Client ID": (
|
||||
<Text
|
||||
w={"60px"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.clientReference_id}
|
||||
</Text>
|
||||
),
|
||||
"First Name": (
|
||||
<Box isTruncated={true} w={"80px"}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"50px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"E-mail ID": (
|
||||
<Box isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.emailAddress}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"100px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.mobileNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Deposit Date": (
|
||||
<Box w={"100px"} isTruncated={true} display={"flex"}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{formatDate(item?.transaction_date)}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Deposit Amount (BHD)": (
|
||||
<Box w={"130px"} isTruncated={true} display={"flex"}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{/* {item.investorAmount} */}
|
||||
{parseFloat(item?.transaction_amount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
{/* <Badge ms={1} colorScheme="green">{item?.transaction_amount}</Badge> */}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Support Image": (
|
||||
<Text
|
||||
color={"green.500"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item?.spportFile_path && (
|
||||
<Badge
|
||||
px={2}
|
||||
py={0.5}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
colorScheme={"forestGreen"}
|
||||
>
|
||||
<Link
|
||||
href={import.meta.env.VITE_IMAGE_URL + item?.spportFile_path}
|
||||
isExternal
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Box me={"1px"} as="span" cursor={"pointer"}>
|
||||
View
|
||||
</Box>
|
||||
<ExternalLinkIcon />
|
||||
</Link>
|
||||
</Badge>
|
||||
)}
|
||||
</Text>
|
||||
),
|
||||
Status: (
|
||||
<Box isTruncated={true} display={"flex"}>
|
||||
<Badge
|
||||
my={1}
|
||||
fontWeight={500}
|
||||
px={2}
|
||||
py={"2px"}
|
||||
rounded={4}
|
||||
colorScheme={
|
||||
item?.transactionStatus === "Approved"
|
||||
? "green"
|
||||
: item?.transactionStatus === "Pending"
|
||||
? "yellow"
|
||||
: item?.transactionStatus === "Reject"
|
||||
? "red"
|
||||
: "gray" // default border color if status doesn't match any condition
|
||||
}
|
||||
>
|
||||
{item.transactionStatus}
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
"Reversal Action": (
|
||||
<Box w={"120px"} isTruncated={true} cursor={"pointer"}>
|
||||
{item.transactionStatus === "Approved" ? (
|
||||
<Text
|
||||
as={"span"}
|
||||
color={!item.isReversal ? "green.500" : "#FFBB00"}
|
||||
fontWeight={700}
|
||||
>
|
||||
{!item.isReversal ? (
|
||||
<Button
|
||||
onClick={() => {
|
||||
onOpenInRev(); // Call the function
|
||||
// setReversalId(item.transaction_xid);
|
||||
setReversalId(item.id);
|
||||
}}
|
||||
colorScheme="teal"
|
||||
size="xs"
|
||||
variant="outline"
|
||||
>
|
||||
Initiate Reversal
|
||||
</Button>
|
||||
) : (
|
||||
"Under process"
|
||||
)}
|
||||
</Text>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedSponsors = sponser.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setSponser(updatedSponsors);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
const handleApproved = async (data) => {
|
||||
setIsReversalLoading.on(); // Start loading
|
||||
try {
|
||||
const { error, data: responseData } = await createFawateerReversalRequest(
|
||||
{
|
||||
id: reversalId,
|
||||
data,
|
||||
}
|
||||
);
|
||||
|
||||
if (error) {
|
||||
throw error; // Explicitly handle the error
|
||||
}
|
||||
|
||||
// Success: Perform necessary actions
|
||||
refetch();
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={responseData?.message || "Action successful!"} />
|
||||
),
|
||||
});
|
||||
onCloseInRev();
|
||||
} catch (error) {
|
||||
// Handle errors
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox
|
||||
message={
|
||||
error?.data?.message || "Something went wrong. Please try again."
|
||||
}
|
||||
status="error"
|
||||
/>
|
||||
),
|
||||
});
|
||||
console.error("Error:", error);
|
||||
} finally {
|
||||
setIsReversalLoading.off(); // Ensure loading is toggled off
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
ps={1}
|
||||
pe={1}
|
||||
pb={4}
|
||||
pt={4}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<Pagination
|
||||
isLoading={drawalRequestLoading}
|
||||
pageSize={pageSize}
|
||||
setPageSize={setPageSize}
|
||||
currentPage={currentPage}
|
||||
setCurrentPage={setCurrentPage}
|
||||
totalItems={data?.data?.totalItems}
|
||||
/>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
isLoading={drawalRequestLoading}
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
// totalPages={10}
|
||||
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<DrawalRequestApprove
|
||||
// data={data?.data?.rows}
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
id={actionId}
|
||||
// firstField={firstField}
|
||||
/>
|
||||
<DrawalRequestReject
|
||||
isOpen={isRejectOpen}
|
||||
onClose={onRejectClose}
|
||||
id={actionId}
|
||||
/>
|
||||
|
||||
<InitiateReversalPopup
|
||||
onClose={onCloseInRev}
|
||||
isOpen={isOpenInRev}
|
||||
handelApproved={handleApproved}
|
||||
isLoading={isReversalLoading}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApproveHistoryMaker;
|
||||
95
src/Pages/FawateerChecker/ApproveHistory/ConfirmHistory.jsx
Normal file
@@ -0,0 +1,95 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
useDisclosure,
|
||||
} from "@chakra-ui/react";
|
||||
import React from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
fees: yup.string().required("File name is required"),
|
||||
totalAmount: yup.string().required("File name is required"),
|
||||
});
|
||||
|
||||
const ConfirmHistory = ({ isOpen, onClose, firstField }) => {
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
const onSubmit = (data) => {
|
||||
console.log(data);
|
||||
setFile(data.document[0]);
|
||||
|
||||
const newDocument = {
|
||||
...data,
|
||||
document: data.document[0].name, // Store the document name
|
||||
status: true,
|
||||
id: uuidv4(),
|
||||
createdAt: new Date().toISOString(),
|
||||
Type: getFileIcon(file.type),
|
||||
};
|
||||
|
||||
setCreate((prevCreate) => [...prevCreate, newDocument]);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const selectedFile = event.target.files[0];
|
||||
setFile(selectedFile);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} initialFocusRef={firstField}>
|
||||
<ModalOverlay />
|
||||
<ModalContent pb={4}>
|
||||
<ModalHeader fontSize={"md"}>Confirm</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mb={4}>
|
||||
<FormLabel fontSize="sm">Withdrawal Amount</FormLabel>
|
||||
<Input
|
||||
focusBorderColor='green.400'
|
||||
name="fileName"
|
||||
{...register("fileName")}
|
||||
fontSize="sm"
|
||||
type="text"
|
||||
size="sm"
|
||||
placeholder={"$100,000"}
|
||||
readOnly
|
||||
/>
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button colorScheme="gray" mr={3} onClick={onClose} size={'sm'} rounded={'sm'}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button colorScheme="forestGreen" variant="solid" size={'sm'} rounded={'sm'}>
|
||||
Confirm
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfirmHistory;
|
||||
|
||||
100
src/Pages/FawateerChecker/ApproveHistory/RejectHistory.jsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
useDisclosure,
|
||||
} from "@chakra-ui/react";
|
||||
import React from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
comment: yup.string().required("Comment is required"),
|
||||
});
|
||||
|
||||
const RejectHistory = ({ isOpen, onClose, firstField }) => {
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
const onSubmit = (data) => {
|
||||
console.log(data);
|
||||
setFile(data.document[0]);
|
||||
|
||||
const newDocument = {
|
||||
...data,
|
||||
document: data.document[0].name, // Store the document name
|
||||
comment: true,
|
||||
id: uuidv4(),
|
||||
Type: getFileIcon(file.type),
|
||||
};
|
||||
|
||||
setCreate((prevCreate) => [...prevCreate, newDocument]);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const selectedFile = event.target.files[0];
|
||||
setFile(selectedFile);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} initialFocusRef={firstField}>
|
||||
<ModalOverlay />
|
||||
<ModalContent pb={4}>
|
||||
<ModalHeader fontSize={"md"}>Reject</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mb={4}>
|
||||
<FormLabel fontSize="sm">Comment</FormLabel>
|
||||
<Textarea rows={6}
|
||||
focusBorderColor='green.400'
|
||||
name="fileName"
|
||||
{...register("fileName")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="md"
|
||||
placeholder={"$100,000"}
|
||||
rounded={'md'}
|
||||
resize={'none'}
|
||||
/>
|
||||
{errors.comment && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.comment.message}
|
||||
</Text>
|
||||
)}
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button colorScheme="gray" mr={3} onClick={onClose} size={'sm'} rounded={'sm'}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button colorScheme="forestGreen" variant="solid" size={'sm'} rounded={'sm'}>
|
||||
Send
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RejectHistory;
|
||||
|
||||
416
src/Pages/FawateerChecker/ApproveRequest/ApproveRequest.jsx
Normal file
@@ -0,0 +1,416 @@
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Link,
|
||||
Text,
|
||||
Tooltip,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { CheckIcon, CloseIcon, ExternalLinkIcon } from "@chakra-ui/icons";
|
||||
import Pagination from "../../../Components/Pagination";
|
||||
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
|
||||
import NormalTable from "../../../Components/DataTable/NormalTable";
|
||||
import { generateSerialNumber } from "../../../Constants/Constants";
|
||||
import { useGetFawateerRequestQuery } from "../../../Services/fawateer.request.service";
|
||||
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
|
||||
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
|
||||
import RequestApproveModal from "./RequestApproveModal";
|
||||
import RequestRejectModal from "./RequestRejectModal";
|
||||
|
||||
const ApproveRequest = () => {
|
||||
const toast = useToast();
|
||||
const { slideFromRight, fawateerRequest, setFawateerRequest } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString('en-GB', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Debounce the search term to avoid making a request on every keystroke
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedSearchTerm(searchTerm);
|
||||
}, 500); // Adjust delay as needed
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [searchTerm]);
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
onOpen: onConfirmOpen,
|
||||
onClose: onConfirmClose,
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isRejectOpen,
|
||||
onOpen: onRejectOpen,
|
||||
onClose: onRejectClose,
|
||||
} = useDisclosure();
|
||||
|
||||
const {
|
||||
data,
|
||||
isLoading: drawalRequestLoading,
|
||||
error,
|
||||
refetch
|
||||
} = useGetFawateerRequestQuery(
|
||||
{
|
||||
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
|
||||
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
|
||||
searchTerm: debouncedSearchTerm,
|
||||
},
|
||||
{
|
||||
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
|
||||
}
|
||||
);
|
||||
|
||||
console.log(data?.data?.rows);
|
||||
|
||||
|
||||
|
||||
|
||||
// Use useEffect to refetch data when the component mounts
|
||||
useEffect(() => {
|
||||
refetch();
|
||||
}, [refetch]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
// ====================================================[Table Filter]================================================================
|
||||
const filteredData = data?.data?.rows?.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item.firstName;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
|
||||
// Filter by status
|
||||
// const status = item.status;
|
||||
// const statusLower = status ? "active" : "inactive";
|
||||
|
||||
// const statusMatches =
|
||||
// statusFilter === "all" ||
|
||||
// (statusFilter === "active" && status === true) ||
|
||||
// (statusFilter === "inactive" && status === false);
|
||||
|
||||
return nameMatches;
|
||||
|
||||
|
||||
});
|
||||
|
||||
const role = localStorage?.getItem('role')
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr.no",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"E-mail ID",
|
||||
"Phone Number",
|
||||
"Deposit Date",
|
||||
"Deposit Amount (BHD)",
|
||||
"Support Image",
|
||||
"Action",
|
||||
];
|
||||
|
||||
|
||||
|
||||
const extractedArray = filteredData?.map((item, idx) => ({
|
||||
// id: item?.id,
|
||||
"Sr.no": (
|
||||
<Text
|
||||
w={"auto"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{generateSerialNumber(idx,currentPage, pageSize )}
|
||||
</Text>
|
||||
),
|
||||
"Client ID": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.clientReference_id}
|
||||
</Text>
|
||||
),
|
||||
"First Name": (
|
||||
<Box isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"E-mail ID": (
|
||||
<Box isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.emailAddress}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"100px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.mobileNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Deposit Date": (
|
||||
<Box isTruncated={true} display={'flex'} >
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{formatDate(item?.transaction_date)}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Deposit Amount (BHD)": (
|
||||
<Box isTruncated={true} display={'flex'} >
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{/* {item.investorAmount} */}
|
||||
{parseFloat(item?.transaction_amount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
{/* <Badge ms={1} colorScheme="green">{item?.transaction_amount}</Badge> */}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Support Image": (
|
||||
<Text
|
||||
color={"green.500"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
<Badge
|
||||
px={2}
|
||||
py={0.5}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
colorScheme={"forestGreen"}
|
||||
>
|
||||
<Link
|
||||
href={import.meta.env.VITE_IMAGE_URL + item?.spportFile_path}
|
||||
isExternal
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Box me={"1px"}
|
||||
as="span"
|
||||
cursor={"pointer"}
|
||||
>
|
||||
View
|
||||
</Box>
|
||||
<ExternalLinkIcon />
|
||||
</Link>
|
||||
</Badge>
|
||||
</Text>
|
||||
),
|
||||
"Status": (
|
||||
<Box isTruncated={true} display={'flex'} >
|
||||
<Badge
|
||||
my={1}
|
||||
fontWeight={500}
|
||||
px={2}
|
||||
py={"2px"}
|
||||
rounded={4}
|
||||
colorScheme={
|
||||
item?.transactionStatus === "Approved"
|
||||
? "green"
|
||||
: item?.transactionStatus === "Pending"
|
||||
? "yellow"
|
||||
: item?.transactionStatus === "Reject"
|
||||
? "red"
|
||||
: "gray" // default border color if status doesn't match any condition
|
||||
}
|
||||
>
|
||||
{item.transactionStatus}
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
Action: (
|
||||
<Box display={"flex"} justifyContent={"center"} gap={2}>
|
||||
<Tooltip
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
label="Approve"
|
||||
bg="#fff"
|
||||
color={"green.500"}
|
||||
placement="left-start"
|
||||
>
|
||||
<Button
|
||||
// colorScheme="forestGreen"
|
||||
// color="green.500"
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={2}
|
||||
py={1}
|
||||
onClick={() => {
|
||||
setActionId(item.id);
|
||||
onConfirmOpen();
|
||||
}}
|
||||
colorScheme="green"
|
||||
variant={"solid"}
|
||||
cursor={"pointer"}
|
||||
>
|
||||
<CheckIcon fontSize={"12px"} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
label="Reject"
|
||||
bg="#fff"
|
||||
color={"red.500"}
|
||||
placement="left-start"
|
||||
>
|
||||
<Button
|
||||
colorScheme="red"
|
||||
// color="red.500"
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={2}
|
||||
onClick={() => {
|
||||
setActionId(item.id);
|
||||
onRejectOpen();
|
||||
}}
|
||||
py={1}
|
||||
// variant={"solid"}
|
||||
>
|
||||
<CloseIcon fontSize={"10px"} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedSponsors = sponser.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setSponser(updatedSponsors);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
ps={1}
|
||||
pe={1}
|
||||
pb={4}
|
||||
pt={4}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<Pagination
|
||||
isLoading={drawalRequestLoading}
|
||||
pageSize={pageSize}
|
||||
setPageSize={setPageSize}
|
||||
currentPage={currentPage}
|
||||
setCurrentPage={setCurrentPage}
|
||||
totalItems={data?.data?.totalItems}
|
||||
/>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
isLoading={drawalRequestLoading}
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
// totalPages={10}
|
||||
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<RequestApproveModal
|
||||
// data={data?.data?.rows}
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
id={actionId}
|
||||
// firstField={firstField}
|
||||
/>
|
||||
<RequestRejectModal
|
||||
isOpen={isRejectOpen}
|
||||
onClose={onRejectClose}
|
||||
id={actionId}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApproveRequest;
|
||||
|
||||
183
src/Pages/FawateerChecker/ApproveRequest/RequestApproveModal.jsx
Normal file
@@ -0,0 +1,183 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useDepositRejectMutation } from "../../../Services/deposit.request.service";
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
import { useApproveCommentMutation } from "../../../Services/fawateer.request.service";
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
// checkerComment: yup.string().required("Comment is required")
|
||||
// .max(50, "Investment name cannot be more than 50 characters"),
|
||||
checkerComment: yup
|
||||
.string()
|
||||
.required("Comment is required")
|
||||
.max(200, "Approve Comment cannot be more than 200 characters"),
|
||||
});
|
||||
|
||||
const RequestApproveModal = ({ isOpen, onClose, firstField ,id}) => {
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
const [ approveFawateer ] = useApproveCommentMutation()
|
||||
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
console.log(data, "tewxttttt");
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await approveFawateer({data,id})
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}else if(res?.data){
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.data?.message} />
|
||||
),
|
||||
});
|
||||
onClose()
|
||||
setIsBtnLoading(false)
|
||||
}else{
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={'error'} message={"Something went wrong"} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const selectedFile = event.target.files[0];
|
||||
setFile(selectedFile);
|
||||
};
|
||||
|
||||
|
||||
const { data, isLoading } =
|
||||
(id, {
|
||||
skip: !id,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
reset({
|
||||
investorAmount: data?.data?.investorAmount,
|
||||
});
|
||||
}
|
||||
}, [data, reset]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
reset();
|
||||
}
|
||||
}, [isOpen, reset]);
|
||||
|
||||
const heandleOnClose = () =>{
|
||||
reset()
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
|
||||
<ModalOverlay />
|
||||
<ModalContent pb={4}>
|
||||
<ModalHeader fontSize={"md"}>Approve Comment</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
{isLoading ? (
|
||||
<FullscreenLoaders height={"50vh"} />
|
||||
) : (
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">Comment</FormLabel>
|
||||
<Textarea
|
||||
rows={6}
|
||||
focusBorderColor="green.400"
|
||||
name="checkerComment"
|
||||
{...register("checkerComment")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="md"
|
||||
placeholder={"Enter your checker Comment...."}
|
||||
rounded={"md"}
|
||||
resize={"none"}
|
||||
maxLength={200}
|
||||
/>
|
||||
{errors.checkerComment && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.checkerComment.message}
|
||||
</Text>
|
||||
)}
|
||||
<FormHelperText fontSize="xs" color="gray.500">
|
||||
Maximum length should be 200 characters. You have entered
|
||||
{watch("checkerComment")?.length || 0} characters.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
colorScheme="gray"
|
||||
mr={3}
|
||||
onClick={onClose}
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RequestApproveModal;
|
||||
189
src/Pages/FawateerChecker/ApproveRequest/RequestRejectModal.jsx
Normal file
@@ -0,0 +1,189 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useDepositRejectMutation } from "../../../Services/deposit.request.service";
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
import { useRejectCommentMutation } from "../../../Services/fawateer.request.service";
|
||||
|
||||
// export const conformModalSchema = yup.object().shape({
|
||||
// comments: yup.string().required("Comment is required")
|
||||
// .max(200, "Approve Comment cannot be more than 200 characters"),
|
||||
// });
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
// checkerComment: yup.string().required("Comment is required")
|
||||
// .max(50, "Investment name cannot be more than 50 characters"),
|
||||
comments: yup
|
||||
.string()
|
||||
.required("Comment is required")
|
||||
.max(200, "Approve Comment cannot be more than 200 characters"),
|
||||
});
|
||||
|
||||
const RequestRejectModal = ({ isOpen, onClose, firstField ,id}) => {
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
reset(); // Clear the form state
|
||||
}
|
||||
}, [isOpen, reset]);
|
||||
|
||||
|
||||
const [ rejectFawateer ] = useRejectCommentMutation()
|
||||
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
console.log(data, "tewxttttt");
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await rejectFawateer({data,id})
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}else if(res?.data){
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.data?.message} />
|
||||
),
|
||||
});
|
||||
onClose()
|
||||
setIsBtnLoading(false)
|
||||
}else{
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={'error'} message={"Something went wrong"} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const selectedFile = event.target.files[0];
|
||||
setFile(selectedFile);
|
||||
};
|
||||
|
||||
|
||||
const { data, isLoading } =
|
||||
(id, {
|
||||
skip: !id,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
reset({
|
||||
investorAmount: data?.data?.investorAmount,
|
||||
});
|
||||
}
|
||||
}, [data, reset]);
|
||||
|
||||
const heandleOnClose = () =>{
|
||||
reset()
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
|
||||
<ModalOverlay />
|
||||
<ModalContent pb={4}>
|
||||
<ModalHeader fontSize={"md"}>Reject Comment</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
{isLoading ? (
|
||||
<FullscreenLoaders height={"50vh"} />
|
||||
) : (
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">Comment</FormLabel>
|
||||
<Textarea
|
||||
rows={6}
|
||||
focusBorderColor="green.400"
|
||||
name="comments"
|
||||
{...register("comments")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="md"
|
||||
placeholder={"Enter your comments...."}
|
||||
rounded={"md"}
|
||||
resize={"none"}
|
||||
maxLength={200}
|
||||
/>
|
||||
{errors.comments && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.comments.message}
|
||||
</Text>
|
||||
)}
|
||||
<FormHelperText fontSize="xs" color="gray.500">
|
||||
Maximum length should be 200 characters. You have entered
|
||||
<Text as={'span'} ml={2}>{watch("comments")?.length || 0} </Text>characters.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
colorScheme="gray"
|
||||
mr={3}
|
||||
onClick={onClose}
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RequestRejectModal;
|
||||
138
src/Pages/ForgetPassword.jsx
Normal 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;
|
||||
@@ -30,14 +30,14 @@ import GlobalStateContext from "../../../Contexts/GlobalStateContext";
|
||||
import CurrencyInput from "../../../Components/CurrencyInput";
|
||||
|
||||
const cashDetails = yup.object().shape({
|
||||
transactionDate: yup.string().required("Artifact name is required"),
|
||||
ioTransType_xid: yup.number().required("Artifact name is required"),
|
||||
transactionAmount: yup.number().required("Artifact name is required"),
|
||||
transactionDate: yup.string().required("Date is required"),
|
||||
ioTransType_xid: yup.number().required("Cash transaction is required"),
|
||||
transactionAmount: yup.number().required("Transaction Amount is required"),
|
||||
comments: yup.string().notRequired(),
|
||||
});
|
||||
|
||||
const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, data }) => {
|
||||
const params = useParams()
|
||||
const params = useParams()
|
||||
const id = params?.id
|
||||
const [file, setFile] = useState("");
|
||||
const [fileName, setFileName] = useState("");
|
||||
@@ -56,7 +56,7 @@ const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, da
|
||||
const [updateVideoArtifacts] = useUpdateVideoArtifactsMutation()
|
||||
// const {
|
||||
// data
|
||||
// } = useGetArtifactsQuery(id)
|
||||
// } = useGetArtifactsQuery(id)
|
||||
|
||||
const {
|
||||
control,
|
||||
@@ -91,11 +91,11 @@ const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, da
|
||||
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
};
|
||||
@@ -113,7 +113,9 @@ const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, da
|
||||
const handleClose = () => {
|
||||
setAlert(false)
|
||||
onClose()
|
||||
reset()
|
||||
reset({
|
||||
transactionAmount:""
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -132,13 +134,14 @@ const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, da
|
||||
|
||||
<DrawerBody>
|
||||
<Stack spacing={4}>
|
||||
<FormControl isInvalid={errors.transactionDate} isRequired>
|
||||
<FormControl isInvalid={errors.transactionDate} isRequired>
|
||||
<FormLabel fontSize={"sm"}>Date Selection</FormLabel>
|
||||
<Controller
|
||||
name="transactionDate"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Input {...field} fontSize={"sm"} type="date" size={"sm"} />
|
||||
<Input
|
||||
focusBorderColor="forestGreen.300" {...field} fontSize={"sm"} type="date" size={"sm"} />
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
@@ -158,6 +161,7 @@ const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, da
|
||||
placeholder="Select an option"
|
||||
fontSize={"sm"}
|
||||
size={"sm"}
|
||||
focusBorderColor="forestGreen.300"
|
||||
>
|
||||
{IODetails?.ioCashTransaction?.map(({ id, transactionName }) => (
|
||||
<option key={id} value={id}>
|
||||
@@ -197,7 +201,8 @@ const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, da
|
||||
name="comments"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textarea {...field} textAlign={'right'} fontSize={"sm"} type="text" size={"sm"} />
|
||||
<Textarea {...field} textAlign={'left'}
|
||||
focusBorderColor="forestGreen.300" fontSize={"sm"} type="text" size={"sm"} />
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
|
||||
@@ -11,10 +11,13 @@ import {
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormLabel,
|
||||
HStack,
|
||||
Input,
|
||||
Select,
|
||||
Stack,
|
||||
Text,
|
||||
Textarea,
|
||||
VStack,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import * as yup from "yup";
|
||||
@@ -31,8 +34,8 @@ import {
|
||||
import { formatDatee } from "../../../Components/FormField";
|
||||
|
||||
const ioNav = yup.object().shape({
|
||||
transactionDate: yup.string().required("Artifact name is required"),
|
||||
transactionAmount: yup.number().required("Artifact name is required"),
|
||||
transactionDate: yup.string().required("Date is required"),
|
||||
transactionAmount: yup.number().required("New NAV is required"),
|
||||
comments: yup.string().notRequired(),
|
||||
});
|
||||
|
||||
@@ -85,10 +88,10 @@ import { formatDatee } from "../../../Components/FormField";
|
||||
});
|
||||
handleClose()
|
||||
}else if(res?.error?.status === 400){
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
|
||||
});
|
||||
handleClose()
|
||||
}
|
||||
|
||||
|
||||
@@ -110,6 +113,7 @@ import { formatDatee } from "../../../Components/FormField";
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setIsLoading(false);
|
||||
setAlert(false)
|
||||
onClose()
|
||||
reset({
|
||||
@@ -122,7 +126,15 @@ import { formatDatee } from "../../../Components/FormField";
|
||||
|
||||
|
||||
|
||||
const today = formatDatee(new Date(), 'yyyy-MM-dd');
|
||||
const today = formatDatee(new Date(), 'yyyy-MM-dd');
|
||||
|
||||
function calculatePercentage(newNav, currNav) {
|
||||
const per = (newNav - currNav) / currNav * 100
|
||||
return per.toFixed(2)
|
||||
}
|
||||
|
||||
|
||||
console.log(calculatePercentage(1092500, 976070));
|
||||
|
||||
|
||||
|
||||
@@ -161,7 +173,7 @@ const today = formatDatee(new Date(), 'yyyy-MM-dd');
|
||||
|
||||
|
||||
<FormControl isInvalid={errors.transactionAmount} isRequired>
|
||||
<FormLabel fontSize={"sm"}>Transaction Amount</FormLabel>
|
||||
<FormLabel fontSize={"sm"}>New NAV</FormLabel>
|
||||
<Controller
|
||||
name="transactionAmount"
|
||||
control={control}
|
||||
@@ -173,7 +185,26 @@ const today = formatDatee(new Date(), 'yyyy-MM-dd');
|
||||
{errors.transactionAmount?.message}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
|
||||
|
||||
|
||||
<HStack justify={'start'} gap={10} bg={'green.100'} p={3} rounded={'md'} shadow={'md'}>
|
||||
<VStack align={'start'}>
|
||||
<Text as={'span'} fontSize={'sm'} fontWeight={500}>Current nav</Text>
|
||||
<Text as={'span'} fontSize={'sm'}>
|
||||
{parseFloat(IODetails?.ioNAV || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Text>
|
||||
</VStack>
|
||||
|
||||
|
||||
<VStack align={'start'}>
|
||||
<Text as={'span'} fontSize={'sm'} fontWeight={500}>Live return %</Text>
|
||||
<Text as={'span'} fontSize={'sm'}>{calculatePercentage(watch()?.transactionAmount||IODetails?.ioNAV,IODetails?.ioNAV)}</Text>
|
||||
</VStack>
|
||||
</HStack>
|
||||
|
||||
|
||||
|
||||
<FormControl isInvalid={errors.comments}>
|
||||
|
||||
@@ -8,21 +8,22 @@ import IODetails from "./IODetails";
|
||||
import KeyMerits from "./KeyMerits";
|
||||
import IOArtifacts from "./IOArtifacts";
|
||||
import Investors from "./Investors";
|
||||
import IOCashDetails from "./IOCashDetails";
|
||||
import IONAVDetails from "./IONAVDetails";
|
||||
// import IOCashDetails from "./IOCashDetailsold";
|
||||
// import IONAVDetails from "./IONAVDetailsOld";
|
||||
import InvestmentDocument from "./InvestmentDocument"; // Ensure this is the correct import
|
||||
import ViewIOdataHeader from "../ViewIO/ViewIOdataHeader";
|
||||
import { useParams } from "react-router-dom";
|
||||
import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
|
||||
import { useGetIOprepopulateDataQuery } from "../../../Services/io.service";
|
||||
import UnderConstruction from "../../UnderConstruction";
|
||||
import Destribution from "./Destribution";
|
||||
import IOCashDetails from "./IOCashDetails/IOCashDetails";
|
||||
import IONAVDetails from "./IONAVDetails/IONAVDetails";
|
||||
import IOTransaction from "./IOTransaction/IOTransaction";
|
||||
|
||||
const CreateIO = () => {
|
||||
const id = useParams()?.id;
|
||||
const { data, error, isLoading } = useGetIOprepopulateDataQuery();
|
||||
|
||||
|
||||
// console.log(data?.data);
|
||||
|
||||
const enableNextTab = (index) => {
|
||||
setTabs((prevTabs) => {
|
||||
@@ -44,38 +45,43 @@ const CreateIO = () => {
|
||||
{
|
||||
label: "Investment documents",
|
||||
Content: InvestmentDocument,
|
||||
isDisabled: id ? false : true,
|
||||
isDisabled: id ? true : true,
|
||||
},
|
||||
{
|
||||
label: "Key merits",
|
||||
Content: KeyMerits,
|
||||
isDisabled: id ? false : true,
|
||||
isDisabled: id ? true : true,
|
||||
},
|
||||
{
|
||||
label: "IO artifacts",
|
||||
Content: IOArtifacts,
|
||||
isDisabled: id ? false : true,
|
||||
isDisabled: id ? true : true,
|
||||
},
|
||||
{
|
||||
label: "Investors",
|
||||
// Content: Investors,
|
||||
Content: UnderConstruction,
|
||||
isDisabled: id ? false : true,
|
||||
Content: Investors,
|
||||
// Content: UnderConstruction,
|
||||
isDisabled: id ? true : true,
|
||||
},
|
||||
{
|
||||
label: "IO Cash Detail",
|
||||
Content: IOCashDetails,
|
||||
isDisabled: id ? false : true,
|
||||
isDisabled: id ? true : true,
|
||||
},
|
||||
{
|
||||
label: "IO NAV Details",
|
||||
Content: IONAVDetails,
|
||||
isDisabled: id ? false : true,
|
||||
isDisabled: id ? true : true,
|
||||
},
|
||||
{
|
||||
label: "Distribution to Investors",
|
||||
Content: IONAVDetails,
|
||||
isDisabled: id ? false : true,
|
||||
Content: Destribution,
|
||||
isDisabled: id ? true : true,
|
||||
},
|
||||
{
|
||||
label: "IO Transaction",
|
||||
Content: IOTransaction,
|
||||
isDisabled: id ? true : true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -116,7 +122,8 @@ const CreateIO = () => {
|
||||
<Tab
|
||||
isDisabled={isDisabled}
|
||||
key={index}
|
||||
fontSize={"sm"}
|
||||
fontSize={"xs"}
|
||||
fontWeight={500}
|
||||
_selected={{
|
||||
color: "#004118",
|
||||
borderBottom: "2px solid #38a169",
|
||||
|
||||
@@ -1,9 +1,238 @@
|
||||
import React from 'react'
|
||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
|
||||
import {
|
||||
Box,
|
||||
HStack,
|
||||
Input,
|
||||
Text,
|
||||
Table,
|
||||
Tbody,
|
||||
Th,
|
||||
Tr,
|
||||
Avatar,
|
||||
useDisclosure,
|
||||
Button,
|
||||
Badge,
|
||||
} from "@chakra-ui/react";
|
||||
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
|
||||
import Pagination from "../../../Components/Pagination";
|
||||
import NormalTable from "../../../Components/DataTable/NormalTable";
|
||||
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
|
||||
import { formatDatee } from "../../../Components/FormField";
|
||||
import { AddIcon } from "@chakra-ui/icons";
|
||||
import AddIONav from "./AddIONav";
|
||||
|
||||
const Destribution = () => {
|
||||
const { navDetails, setNavDetails, IODetails } =
|
||||
useContext(GlobalStateContext);
|
||||
const firstField = useRef();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
|
||||
console.log(IODetails?.ioNAVHistory);
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
// Table setup
|
||||
const tableHeadRow = [
|
||||
// "Sr.No",
|
||||
"Date",
|
||||
"Amount",
|
||||
"% of Investment",
|
||||
];
|
||||
|
||||
// Table filter
|
||||
const filteredData = IODetails?.distributionToInvestor
|
||||
?.filter((item) => {
|
||||
const name = item?.transactionAmount;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
return nameMatches;
|
||||
})
|
||||
.sort((b, a) => new Date(a.transactionDate) - new Date(b.transactionDate));
|
||||
|
||||
const extractedArray = filteredData?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr.No": (
|
||||
<Text
|
||||
justifyContent={"start"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-start web-text-small"
|
||||
>
|
||||
{item?.id}
|
||||
</Text>
|
||||
),
|
||||
Date: (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{formatDate(item.transactionDate)}
|
||||
</Text>
|
||||
),
|
||||
Amount: (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{`${parseFloat(item.transactionAmount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}`}
|
||||
</Text>
|
||||
),
|
||||
"% of Investment": (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{parseFloat(item.percentage).toFixed(2)}%
|
||||
</Text>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedNav = navDetails.filter((sponsor) => sponsor.id !== actionId);
|
||||
|
||||
setTimeout(() => {
|
||||
setNavDetails(updatedNav);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
const Total = () => {
|
||||
return (
|
||||
<Table size="sm">
|
||||
<Tbody>
|
||||
<Tr backgroundColor="gray.50">
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="60px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
Total
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="90px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{IODetails?.total_distributeToInvestor_amt?.toLocaleString(
|
||||
undefined,
|
||||
{ minimumFractionDigits: 2, maximumFractionDigits: 2 }
|
||||
)}
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="100px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
{" "}
|
||||
</Th>
|
||||
</Tr>
|
||||
</Tbody>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
const Distribution = () => {
|
||||
return (
|
||||
<div>Distribution</div>
|
||||
)
|
||||
}
|
||||
<Box {...OPACITY_ON_LOAD} pb={0}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
pb={3}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
export default Distribution
|
||||
<NormalTable
|
||||
centered={true}
|
||||
emptyMessage={`We don't have any Sponers`}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
total={<Total />}
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
|
||||
<AddIONav isOpen={isOpen} onClose={onClose} firstField={firstField} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Destribution;
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
} from "@chakra-ui/icons";
|
||||
import IOArtifactsAdd from "../IOArtifactsAdd";
|
||||
import IOArtifactsVideo from "./IOArtifactsVideo";
|
||||
import SetDisplayOrder from "./SetDisplayOrder";
|
||||
import SetDisplayOrder from "./SetDisplayOrderKeyMerits";
|
||||
import { useParams } from "react-router-dom";
|
||||
import {
|
||||
useDeleteImageArtifactsMutation,
|
||||
@@ -32,6 +32,8 @@ import {
|
||||
import { getFileNameFromPath } from "../../../Constants/Constants";
|
||||
import ImageViewer from "../../../Components/ImageViewer";
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
import SetDisplayOrderIOArtifactsImages from "./SetDisplayOrderIOArtifactsImages";
|
||||
import SetDisplayOrderIOArtifactsVideo from "./SetDisplayOrderIOArtifactsVideo";
|
||||
|
||||
const IOArtifacts = ({ enableNextTab, index, data }) => {
|
||||
const toast = useToast()
|
||||
@@ -114,7 +116,7 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
|
||||
setDeleteAlertVideo(false);
|
||||
setIsLoadingBtn(false);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.data?.message} status="error" />,
|
||||
render: () => <ToastBox message={res?.data?.message} status="success" />,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -132,7 +134,7 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
|
||||
setDeleteAlertImage(false);
|
||||
setIsLoadingBtn(false);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.data?.message} status="error" />,
|
||||
render: () => <ToastBox message={res?.data?.message} status="success" />,
|
||||
});
|
||||
|
||||
}
|
||||
@@ -143,7 +145,14 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
|
||||
|
||||
const tableHeadRow = ["Sr.no", "File Name", "View image", "Action"];
|
||||
|
||||
const extractedArray = IObyID?.data?.artifactsImage?.map((item, index) => ({
|
||||
console.log(IObyID?.data?.artifactsImage);
|
||||
// console.log(filteredData);
|
||||
const sortedDataImage = [...(IObyID?.data?.artifactsImage || [])]?.sort(
|
||||
(a, b) => a?.displayOrder - b?.displayOrder
|
||||
);
|
||||
|
||||
|
||||
const extractedArray = sortedDataImage?.map((item, index) => ({
|
||||
"Sr.no": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
@@ -180,7 +189,7 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
|
||||
colorScheme={"forestGreen"}
|
||||
>
|
||||
<Link
|
||||
href={"https://tanami.betadelivery.com/" + item?.artifactPathName}
|
||||
href={import.meta.env.VITE_IMAGE_URL + item?.artifactPathName}
|
||||
isExternal
|
||||
>
|
||||
<Box
|
||||
@@ -246,6 +255,17 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
|
||||
),
|
||||
}));
|
||||
|
||||
|
||||
console.log(IObyID?.data?.artifactsVideo);
|
||||
// console.log(filteredData);
|
||||
const sortedDataVideo = [...(IObyID?.data?.artifactsVideo || [])]?.sort(
|
||||
(a, b) => a?.displayOrder - b?.displayOrder
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const tableHeadRowTwo = [
|
||||
"Sr.no",
|
||||
"File Name",
|
||||
@@ -253,7 +273,7 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
|
||||
"Action",
|
||||
];
|
||||
|
||||
const extractedArrayTwo = IObyID?.data?.artifactsVideo?.map(
|
||||
const extractedArrayTwo = sortedDataVideo?.map(
|
||||
(item, index) => ({
|
||||
"Sr.no": (
|
||||
<Text
|
||||
@@ -352,9 +372,9 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
|
||||
Manage IO Images
|
||||
</Box>
|
||||
<HStack>
|
||||
|
||||
|
||||
|
||||
{IObyID?.data?.artifactsImage?.length !== 0 &&<SetDisplayOrder data={IObyID?.data?.artifactsImage} />}
|
||||
{IObyID?.data?.artifactsImage?.length !== 0 &&<SetDisplayOrderIOArtifactsImages data={sortedDataImage} />}
|
||||
<Button
|
||||
leftIcon={<AddIcon />}
|
||||
onClick={onOpen}
|
||||
@@ -390,7 +410,7 @@ const IOArtifacts = ({ enableNextTab, index, data }) => {
|
||||
Manage IO videos
|
||||
</Box>
|
||||
<HStack>
|
||||
{IObyID?.data?.artifactsVideo?.length !== 0 &&<SetDisplayOrder data={IObyID?.data?.artifactsVideo} />}
|
||||
{IObyID?.data?.artifactsVideo?.length !== 0 &&<SetDisplayOrderIOArtifactsVideo data={sortedDataVideo} />}
|
||||
<Button
|
||||
leftIcon={<AddIcon />}
|
||||
onClick={onOpenVideo}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
DrawerOverlay,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Input,
|
||||
Stack,
|
||||
@@ -26,11 +27,11 @@ import { useParams } from "react-router-dom";
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
|
||||
const investmentVideoSchema = yup.object().shape({
|
||||
artifactName: yup.string().required("Artifact name is required"),
|
||||
artifactName: yup.string().required("Artifact name is required")
|
||||
.max(50, "Approve Comment cannot be more than 50 characters"),
|
||||
artifactStreamingURL: yup.string()
|
||||
.required("Artifact streaming URL is required")
|
||||
.url("Invalid URL format")
|
||||
.matches(/\.mp4$/, "URL must end with .mp4"),
|
||||
.url("Invalid URL format"),
|
||||
});
|
||||
|
||||
const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, data }) => {
|
||||
@@ -153,21 +154,25 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, da
|
||||
|
||||
<DrawerBody>
|
||||
<Stack spacing={4}>
|
||||
<FormControl isInvalid={errors.artifactName}>
|
||||
<FormControl isInvalid={errors.artifactName} isRequired>
|
||||
<FormLabel fontSize={"sm"}>Artifact Name</FormLabel>
|
||||
<Controller
|
||||
name="artifactName"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Input {...field} fontSize={"sm"} type="text" size={"sm"} />
|
||||
<Input {...field} fontSize={"sm"} type="text" size={"sm"} maxLength={50} />
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.artifactName?.message}
|
||||
</FormErrorMessage>
|
||||
<FormHelperText fontSize="xs" color="gray.500">
|
||||
<Box as="span" me={1}>Maximum length should be 50 characters. You have entered </Box>
|
||||
{watch("artifactName")?.length || 0} characters.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
<FormControl isInvalid={errors.artifactStreamingURL}>
|
||||
<FormControl isInvalid={errors.artifactStreamingURL} isRequired>
|
||||
<FormLabel fontSize={"sm"}>Artifact Streaming URL</FormLabel>
|
||||
<Controller
|
||||
name="artifactStreamingURL"
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Drawer,
|
||||
DrawerBody,
|
||||
DrawerCloseButton,
|
||||
DrawerContent,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerOverlay,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Input,
|
||||
Select,
|
||||
Stack,
|
||||
Textarea,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import * as yup from "yup";
|
||||
import React, { useState, useEffect, useContext } from "react";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { useParams } from "react-router-dom";
|
||||
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
|
||||
import { useCreateIoCashMutation, useCreateVideoArtifactsMutation, useUpdateVideoArtifactsMutation } from "../../../../Services/io.service";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import CurrencyInput from "../../../../Components/CurrencyInput";
|
||||
|
||||
const cashDetails = yup.object().shape({
|
||||
transactionDate: yup.string().required("Date is required"),
|
||||
ioTransType_xid: yup.number().required("Cash transaction is required"),
|
||||
transactionAmount: yup.number().required("Transaction Amount is required"),
|
||||
comments: yup.string().notRequired()
|
||||
.max(200, "Approve Comment cannot be more than 200 characters"),
|
||||
});
|
||||
|
||||
const AddCaseDetails = ({ isOpen, onClose, firstField, actionId, setActionId, data, setActiveTab }) => {
|
||||
const params = useParams()
|
||||
const id = params?.id
|
||||
const [file, setFile] = useState("");
|
||||
const [fileName, setFileName] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [alert, setAlert] = useState(false);
|
||||
const toast = useToast();
|
||||
|
||||
console.log(isOpen);
|
||||
|
||||
|
||||
|
||||
|
||||
// ======================[ Cotext Api ]
|
||||
const { IODetails } = useContext(GlobalStateContext);
|
||||
const found = data?.find((item) => item?.id === actionId);
|
||||
|
||||
|
||||
const [createArtifactsVideo] = useCreateVideoArtifactsMutation()
|
||||
const [updateVideoArtifacts] = useUpdateVideoArtifactsMutation()
|
||||
// const {
|
||||
// data
|
||||
// } = useGetArtifactsQuery(id)
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
watch,
|
||||
reset,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(cashDetails),
|
||||
});
|
||||
|
||||
|
||||
const [createIoCash] = useCreateIoCashMutation()
|
||||
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
|
||||
setIsLoading(true)
|
||||
|
||||
try {
|
||||
|
||||
const res = await createIoCash({ data, id })
|
||||
if (res?.data) {
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.data?.message} />,
|
||||
});
|
||||
handleClose()
|
||||
setActiveTab(1)
|
||||
}else if(res?.error){
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleConfirm = () => {
|
||||
handleSubmit(onSubmit)();
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
handleSubmit(onSubmit)();
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAlert(false)
|
||||
onClose()
|
||||
reset({
|
||||
transactionAmount:""
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Drawer
|
||||
size={"md"}
|
||||
isOpen={isOpen}
|
||||
placement="right"
|
||||
initialFocusRef={firstField}
|
||||
onClose={handleClose}
|
||||
>
|
||||
<DrawerOverlay />
|
||||
<DrawerContent>
|
||||
<DrawerCloseButton />
|
||||
<DrawerHeader fontSize={"sm"}>IO Cash Details</DrawerHeader>
|
||||
|
||||
<DrawerBody>
|
||||
<Stack spacing={4}>
|
||||
<FormControl isInvalid={errors.transactionDate} isRequired>
|
||||
<FormLabel fontSize={"sm"}>Date Selection</FormLabel>
|
||||
<Controller
|
||||
name="transactionDate"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
focusBorderColor="forestGreen.300" {...field} fontSize={"sm"} type="date" size={"sm"} />
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.transactionDate?.message}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
|
||||
|
||||
<FormControl isInvalid={errors.ioTransType_xid} isRequired>
|
||||
<FormLabel fontSize={"sm"}>Cash transaction</FormLabel>
|
||||
<Controller
|
||||
name="ioTransType_xid"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
{...field}
|
||||
placeholder="Select an option"
|
||||
fontSize={"sm"}
|
||||
size={"sm"}
|
||||
focusBorderColor="forestGreen.300"
|
||||
>
|
||||
{IODetails?.ioCashTransaction?.map(({ id, transactionName }) => (
|
||||
<option key={id} value={id}>
|
||||
{transactionName}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.ioTransType_xid?.message}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
|
||||
|
||||
|
||||
|
||||
<FormControl isInvalid={errors.transactionAmount} isRequired>
|
||||
<FormLabel fontSize={"sm"}>Transaction Amount</FormLabel>
|
||||
<Controller
|
||||
name="transactionAmount"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<CurrencyInput {...field} textAlign={'right'} fontSize={"sm"} type="number" size={"sm"} />
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.transactionAmount?.message}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
|
||||
|
||||
|
||||
<FormControl isInvalid={errors.comments}>
|
||||
<FormLabel fontSize={"sm"}>Comment</FormLabel>
|
||||
<Controller
|
||||
name="comments"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textarea {...field} textAlign={'left'}
|
||||
maxLength={200}
|
||||
focusBorderColor="forestGreen.300" fontSize={"sm"} type="text" size={"sm"} />
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.comments?.message}
|
||||
</FormErrorMessage>
|
||||
<FormHelperText fontSize="xs" color="gray.500">
|
||||
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
|
||||
{watch("comments")?.length || 0} characters.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
</Stack>
|
||||
</DrawerBody>
|
||||
|
||||
<DrawerFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
colorScheme={"forestGreen"}
|
||||
rounded={"sm"}
|
||||
size={"sm"}
|
||||
mr={3}
|
||||
onClick={handleClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
colorScheme={"forestGreen"}
|
||||
rounded={"sm"}
|
||||
size={"sm"}
|
||||
|
||||
onClick={() => setAlert(true)}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
|
||||
<CustomAlertDialog
|
||||
isOpen={alert}
|
||||
onClose={() => setAlert(false)}
|
||||
alertHandler={handleSave}
|
||||
message={"Are you sure you want to add cash details?"}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddCaseDetails;
|
||||
251
src/Pages/IO_Management/CreateIO/IOCashDetails/AddPending.jsx
Normal file
@@ -0,0 +1,251 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Drawer,
|
||||
DrawerBody,
|
||||
DrawerCloseButton,
|
||||
DrawerContent,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerOverlay,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormLabel,
|
||||
Input,
|
||||
Select,
|
||||
Stack,
|
||||
Textarea,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import * as yup from "yup";
|
||||
import React, { useState, useEffect, useContext } from "react";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { useParams } from "react-router-dom";
|
||||
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
|
||||
import { useCreateIoCashMutation, useCreateVideoArtifactsMutation, useUpdateVideoArtifactsMutation } from "../../../../Services/io.service";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import CurrencyInput from "../../../../Components/CurrencyInput";
|
||||
|
||||
const cashDetails = yup.object().shape({
|
||||
transactionDate: yup.string().required("Date is required"),
|
||||
ioTransType_xid: yup.number().required("Cash transaction is required"),
|
||||
transactionAmount: yup.number().required("Transaction Amount is required"),
|
||||
comments: yup.string().notRequired(),
|
||||
});
|
||||
|
||||
const AddPending = ({ isOpen, onClose, firstField, actionId, setActionId, data }) => {
|
||||
const params = useParams()
|
||||
const id = params?.id
|
||||
const [file, setFile] = useState("");
|
||||
const [fileName, setFileName] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [alert, setAlert] = useState(false);
|
||||
const toast = useToast();
|
||||
|
||||
|
||||
|
||||
// ======================[ Cotext Api ]
|
||||
const { IODetails } = useContext(GlobalStateContext);
|
||||
const found = data?.find((item) => item?.id === actionId);
|
||||
|
||||
|
||||
const [createArtifactsVideo] = useCreateVideoArtifactsMutation()
|
||||
const [updateVideoArtifacts] = useUpdateVideoArtifactsMutation()
|
||||
// const {
|
||||
// data
|
||||
// } = useGetArtifactsQuery(id)
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
watch,
|
||||
reset,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(cashDetails),
|
||||
});
|
||||
|
||||
|
||||
const [createIoCash] = useCreateIoCashMutation()
|
||||
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
|
||||
setIsLoading(true)
|
||||
|
||||
try {
|
||||
|
||||
const res = await createIoCash({ data, id })
|
||||
if (res?.data?.statusCode === 200) {
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.data?.message} />,
|
||||
});
|
||||
handleClose()
|
||||
}else if(res?.error?.status === 400){
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleConfirm = () => {
|
||||
handleSubmit(onSubmit)();
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
handleSubmit(onSubmit)();
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAlert(false)
|
||||
onClose()
|
||||
reset({
|
||||
transactionAmount:""
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Drawer
|
||||
size={"md"}
|
||||
isOpen={isOpen}
|
||||
placement="right"
|
||||
initialFocusRef={firstField}
|
||||
onClose={handleClose}
|
||||
>
|
||||
<DrawerOverlay />
|
||||
<DrawerContent>
|
||||
<DrawerCloseButton />
|
||||
<DrawerHeader fontSize={"sm"}>IO Cash Details</DrawerHeader>
|
||||
|
||||
<DrawerBody>
|
||||
<Stack spacing={4}>
|
||||
<FormControl isInvalid={errors.transactionDate} isRequired>
|
||||
<FormLabel fontSize={"sm"}>Date Selection</FormLabel>
|
||||
<Controller
|
||||
name="transactionDate"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
focusBorderColor="forestGreen.300" {...field} fontSize={"sm"} type="date" size={"sm"} />
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.transactionDate?.message}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
|
||||
|
||||
<FormControl isInvalid={errors.ioTransType_xid} isRequired>
|
||||
<FormLabel fontSize={"sm"}>Cash transaction</FormLabel>
|
||||
<Controller
|
||||
name="ioTransType_xid"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
{...field}
|
||||
placeholder="Select an option"
|
||||
fontSize={"sm"}
|
||||
size={"sm"}
|
||||
focusBorderColor="forestGreen.300"
|
||||
>
|
||||
{IODetails?.ioCashTransaction?.map(({ id, transactionName }) => (
|
||||
<option key={id} value={id}>
|
||||
{transactionName}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.ioTransType_xid?.message}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
|
||||
|
||||
|
||||
|
||||
<FormControl isInvalid={errors.transactionAmount} isRequired>
|
||||
<FormLabel fontSize={"sm"}>Transaction Amount</FormLabel>
|
||||
<Controller
|
||||
name="transactionAmount"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<CurrencyInput {...field} textAlign={'right'} fontSize={"sm"} type="number" size={"sm"} />
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.transactionAmount?.message}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
|
||||
|
||||
|
||||
<FormControl isInvalid={errors.comments}>
|
||||
<FormLabel fontSize={"sm"}>Comments</FormLabel>
|
||||
<Controller
|
||||
name="comments"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textarea {...field} textAlign={'left'}
|
||||
focusBorderColor="forestGreen.300" fontSize={"sm"} type="text" size={"sm"} />
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.comments?.message}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
|
||||
</Stack>
|
||||
</DrawerBody>
|
||||
|
||||
<DrawerFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
colorScheme={"forestGreen"}
|
||||
rounded={"sm"}
|
||||
size={"sm"}
|
||||
mr={3}
|
||||
onClick={handleClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
colorScheme={"forestGreen"}
|
||||
rounded={"sm"}
|
||||
size={"sm"}
|
||||
|
||||
onClick={() => setAlert(true)}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
|
||||
<CustomAlertDialog
|
||||
isOpen={alert}
|
||||
onClose={() => setAlert(false)}
|
||||
alertHandler={handleSave}
|
||||
message={"Are you sure you want to add cash details?"}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddPending;
|
||||
251
src/Pages/IO_Management/CreateIO/IOCashDetails/AddRejected.jsx
Normal file
@@ -0,0 +1,251 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Drawer,
|
||||
DrawerBody,
|
||||
DrawerCloseButton,
|
||||
DrawerContent,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerOverlay,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormLabel,
|
||||
Input,
|
||||
Select,
|
||||
Stack,
|
||||
Textarea,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import * as yup from "yup";
|
||||
import React, { useState, useEffect, useContext } from "react";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { useParams } from "react-router-dom";
|
||||
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
|
||||
import { useCreateIoCashMutation, useCreateVideoArtifactsMutation, useUpdateVideoArtifactsMutation } from "../../../../Services/io.service";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import CurrencyInput from "../../../../Components/CurrencyInput";
|
||||
|
||||
const cashDetails = yup.object().shape({
|
||||
transactionDate: yup.string().required("Date is required"),
|
||||
ioTransType_xid: yup.number().required("Cash transaction is required"),
|
||||
transactionAmount: yup.number().required("Transaction Amount is required"),
|
||||
comments: yup.string().notRequired(),
|
||||
});
|
||||
|
||||
const AddRejected = ({ isOpen, onClose, firstField, actionId, setActionId, data }) => {
|
||||
const params = useParams()
|
||||
const id = params?.id
|
||||
const [file, setFile] = useState("");
|
||||
const [fileName, setFileName] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [alert, setAlert] = useState(false);
|
||||
const toast = useToast();
|
||||
|
||||
|
||||
|
||||
// ======================[ Cotext Api ]
|
||||
const { IODetails } = useContext(GlobalStateContext);
|
||||
const found = data?.find((item) => item?.id === actionId);
|
||||
|
||||
|
||||
const [createArtifactsVideo] = useCreateVideoArtifactsMutation()
|
||||
const [updateVideoArtifacts] = useUpdateVideoArtifactsMutation()
|
||||
// const {
|
||||
// data
|
||||
// } = useGetArtifactsQuery(id)
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
watch,
|
||||
reset,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(cashDetails),
|
||||
});
|
||||
|
||||
|
||||
const [createIoCash] = useCreateIoCashMutation()
|
||||
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
|
||||
setIsLoading(true)
|
||||
|
||||
try {
|
||||
|
||||
const res = await createIoCash({ data, id })
|
||||
if (res?.data?.statusCode === 200) {
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.data?.message} />,
|
||||
});
|
||||
handleClose()
|
||||
}else if(res?.error?.status === 400){
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleConfirm = () => {
|
||||
handleSubmit(onSubmit)();
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
handleSubmit(onSubmit)();
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAlert(false)
|
||||
onClose()
|
||||
reset({
|
||||
transactionAmount:""
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Drawer
|
||||
size={"md"}
|
||||
isOpen={isOpen}
|
||||
placement="right"
|
||||
initialFocusRef={firstField}
|
||||
onClose={handleClose}
|
||||
>
|
||||
<DrawerOverlay />
|
||||
<DrawerContent>
|
||||
<DrawerCloseButton />
|
||||
<DrawerHeader fontSize={"sm"}>IO Cash Details</DrawerHeader>
|
||||
|
||||
<DrawerBody>
|
||||
<Stack spacing={4}>
|
||||
<FormControl isInvalid={errors.transactionDate} isRequired>
|
||||
<FormLabel fontSize={"sm"}>Date Selection</FormLabel>
|
||||
<Controller
|
||||
name="transactionDate"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
focusBorderColor="forestGreen.300" {...field} fontSize={"sm"} type="date" size={"sm"} />
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.transactionDate?.message}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
|
||||
|
||||
<FormControl isInvalid={errors.ioTransType_xid} isRequired>
|
||||
<FormLabel fontSize={"sm"}>Cash transaction</FormLabel>
|
||||
<Controller
|
||||
name="ioTransType_xid"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
{...field}
|
||||
placeholder="Select an option"
|
||||
fontSize={"sm"}
|
||||
size={"sm"}
|
||||
focusBorderColor="forestGreen.300"
|
||||
>
|
||||
{IODetails?.ioCashTransaction?.map(({ id, transactionName }) => (
|
||||
<option key={id} value={id}>
|
||||
{transactionName}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.ioTransType_xid?.message}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
|
||||
|
||||
|
||||
|
||||
<FormControl isInvalid={errors.transactionAmount} isRequired>
|
||||
<FormLabel fontSize={"sm"}>Transaction Amount</FormLabel>
|
||||
<Controller
|
||||
name="transactionAmount"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<CurrencyInput {...field} textAlign={'right'} fontSize={"sm"} type="number" size={"sm"} />
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.transactionAmount?.message}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
|
||||
|
||||
|
||||
<FormControl isInvalid={errors.comments}>
|
||||
<FormLabel fontSize={"sm"}>Comments</FormLabel>
|
||||
<Controller
|
||||
name="comments"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textarea {...field} textAlign={'left'}
|
||||
focusBorderColor="forestGreen.300" fontSize={"sm"} type="text" size={"sm"} />
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.comments?.message}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
|
||||
</Stack>
|
||||
</DrawerBody>
|
||||
|
||||
<DrawerFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
colorScheme={"forestGreen"}
|
||||
rounded={"sm"}
|
||||
size={"sm"}
|
||||
mr={3}
|
||||
onClick={handleClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
colorScheme={"forestGreen"}
|
||||
rounded={"sm"}
|
||||
size={"sm"}
|
||||
|
||||
onClick={() => setAlert(true)}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
|
||||
<CustomAlertDialog
|
||||
isOpen={alert}
|
||||
onClose={() => setAlert(false)}
|
||||
alertHandler={handleSave}
|
||||
message={"Are you sure you want to add cash details?"}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddRejected;
|
||||
397
src/Pages/IO_Management/CreateIO/IOCashDetails/Approved.jsx
Normal file
@@ -0,0 +1,397 @@
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Table,
|
||||
Tag,
|
||||
Tbody,
|
||||
Text,
|
||||
Th,
|
||||
Tooltip,
|
||||
Tr,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||
import { AddIcon, DeleteIcon, EditIcon, ViewIcon } from "@chakra-ui/icons";
|
||||
import { LuFileSpreadsheet } from "react-icons/lu";
|
||||
import * as XLSX from "xlsx";
|
||||
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
|
||||
import NormalTable from "../../../../Components/DataTable/NormalTable";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import AddCashDetails from "../AddCashDetails";
|
||||
import { debounce } from "../../../Admin/Contact";
|
||||
import AddApproved from "./AddCaseDetails";
|
||||
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
|
||||
import { useParams } from "react-router-dom";
|
||||
import AddCaseDetails from "./AddCaseDetails";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString();
|
||||
|
||||
const Approved = () => {
|
||||
const firstField = useRef();
|
||||
const params = useParams();
|
||||
const toast = useToast();
|
||||
const id = params?.id;
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { IODetails, approved, setApproved } = useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
console.log("==============", IODetails?.ioCashStatusHistory?.Approved);
|
||||
|
||||
// Table filter
|
||||
const filteredData = IODetails?.ioCashStatusHistory?.Approved?.filter(
|
||||
(item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item.transactionDate;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
return nameMatches;
|
||||
}
|
||||
);
|
||||
|
||||
const [updateIOCase] = useUpdateIOCaseMutation();
|
||||
|
||||
const tableHeadRow = [
|
||||
"Sr No.",
|
||||
"Transaction Date",
|
||||
"Transaction Type",
|
||||
"Amount",
|
||||
"Comments",
|
||||
"Update By",
|
||||
"Update On",
|
||||
];
|
||||
|
||||
const extractedArray = filteredData?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr No.": (
|
||||
<Text as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
{index + 1}.
|
||||
</Text>
|
||||
),
|
||||
"Transaction Date": (
|
||||
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
|
||||
{formatDate(item?.transactionDate)}
|
||||
</Text>
|
||||
),
|
||||
"Transaction Type": (
|
||||
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
|
||||
{item?.transactionType}
|
||||
</Text>
|
||||
),
|
||||
Amount: (
|
||||
<Text as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Text>
|
||||
),
|
||||
Comments: (
|
||||
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
{item?.comments ? item?.comments : "---"}
|
||||
</Text>
|
||||
),
|
||||
"Update By": (
|
||||
<Text
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
{/* <Avatar
|
||||
mr={2}
|
||||
size="sm"
|
||||
name={item.creator?.firstName}
|
||||
src={item.creator?.profilePhoto}
|
||||
/> */}
|
||||
{item?.modifier?.firstName}
|
||||
</Text>
|
||||
),
|
||||
"Update On": (
|
||||
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
{formatDate(item.updatedAt)}
|
||||
</Text>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedSponsors = sponser.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setCaseDetails(updatedSponsors);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
const ioCashExporteDetails = IODetails?.ioCashStatusHistory?.Approved?.map(
|
||||
(item, index) => ({
|
||||
"Transaction date": item?.transactionDate,
|
||||
"Transaction type": item?.transactionType,
|
||||
Amount: parseFloat(item?.transactionAmount) || 0,
|
||||
Comments: item?.comments,
|
||||
})
|
||||
);
|
||||
|
||||
const exportToExcelNew = (data, fileName) => {
|
||||
const worksheet = XLSX.utils.json_to_sheet(data);
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
|
||||
|
||||
// Export file
|
||||
XLSX.writeFile(workbook, `${fileName}.xlsx`);
|
||||
};
|
||||
|
||||
const Total = () => {
|
||||
return (
|
||||
<Table size="sm">
|
||||
<Tbody backgroundColor="gray.50">
|
||||
<Tr>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="200px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
Balance in IO Cash
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="120px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
{" "}
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="120px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
{" "}
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="140px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{/* {IODetails?.ioCash} */}
|
||||
{parseFloat(IODetails?.ioCash || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="120px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
{}
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="100px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
{" "}
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="100px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
></Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="100px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
></Th>
|
||||
</Tr>
|
||||
</Tbody>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
const handleAdd = async () => {
|
||||
try {
|
||||
const res = await updateIOCase(id);
|
||||
if (res?.data) {
|
||||
// toast({
|
||||
// render: () => (
|
||||
// <ToastBox status={"success"} message={res?.data?.message} />
|
||||
// ),
|
||||
// });
|
||||
setIsLoading(false);
|
||||
onOpen();
|
||||
} else if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} pb={0}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
pb={3}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<HStack display={"flex"} alignItems={"center"}>
|
||||
<Button
|
||||
onClick={() =>
|
||||
exportToExcelNew(ioCashExporteDetails, "IO Cash History")
|
||||
}
|
||||
leftIcon={<LuFileSpreadsheet />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
variant={"outline"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
isDisabled={ioCashExporteDetails?.length === 0}
|
||||
>
|
||||
Export xls
|
||||
</Button>
|
||||
{/* <Button
|
||||
onClick={onOpen}
|
||||
leftIcon={<AddIcon />}
|
||||
colorScheme={"forestGreen"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
size={"sm"}
|
||||
fontWeight={500}
|
||||
>
|
||||
Add
|
||||
</Button> */}
|
||||
{/* {IODetails?.isInvestedAmount ? (
|
||||
localStorage?.getItem('role') ==="Maker" && <Button
|
||||
onClick={handleAdd}
|
||||
leftIcon={<AddIcon />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
) : null} */}
|
||||
</HStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
emptyMessage={`We don't have any Sponers`}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
total={<Total />}
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
{/* <CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/> */}
|
||||
|
||||
<AddCaseDetails
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
firstField={firstField}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Approved;
|
||||
147
src/Pages/IO_Management/CreateIO/IOCashDetails/IOCashDetails.jsx
Normal file
@@ -0,0 +1,147 @@
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
Tab,
|
||||
TabList,
|
||||
TabPanel,
|
||||
TabPanels,
|
||||
Tabs,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useRef, useState } from "react";
|
||||
import Approved from "./Approved";
|
||||
import Pending from "./Pending";
|
||||
import Rejected from "./Rejected";
|
||||
import { AddIcon } from "@chakra-ui/icons";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import AddCaseDetails from "./AddCaseDetails";
|
||||
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { encryptString, isMaker } from "../../../../Constants/Constants";
|
||||
|
||||
const IOCashDetails = () => {
|
||||
const params = useParams();
|
||||
const toast = useToast();
|
||||
const id = params?.id;
|
||||
const { IODetails } = useContext(GlobalStateContext);
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const firstField = useRef();
|
||||
|
||||
const [updateIOCase] = useUpdateIOCaseMutation();
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
|
||||
const handleAdd = async () => {
|
||||
try {
|
||||
const res = await updateIOCase(id);
|
||||
if (res?.data) {
|
||||
// toast({
|
||||
// render: () => (
|
||||
// <ToastBox status={"success"} message={"res?.data?.message"} />
|
||||
// ),
|
||||
// });
|
||||
// setIsLoading(false);
|
||||
onOpen();
|
||||
} else if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Tabs
|
||||
index={activeTab}
|
||||
onChange={(index) => setActiveTab(index)}
|
||||
variant="unstyled"
|
||||
>
|
||||
<Box
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
alignItems={"center"}
|
||||
borderBottom={"1px solid #ccc"}
|
||||
>
|
||||
<TabList>
|
||||
<Tab
|
||||
fontSize={"sm"}
|
||||
_selected={{
|
||||
color: "#004118",
|
||||
borderBottom: "2px solid #38a169",
|
||||
}}
|
||||
>
|
||||
Approved
|
||||
</Tab>
|
||||
<Tab
|
||||
fontSize={"sm"}
|
||||
_selected={{
|
||||
color: "#004118",
|
||||
borderBottom: "2px solid #38a169",
|
||||
}}
|
||||
>
|
||||
Pending
|
||||
{IODetails?.ioCashStatusHistory?.Pending.length > 0 && (
|
||||
<Badge rounded={"sm"} colorScheme="forestGreen" ms={2}>
|
||||
{IODetails?.ioCashStatusHistory?.Pending.length !== 0 &&
|
||||
IODetails?.ioCashStatusHistory?.Pending.length}
|
||||
</Badge>
|
||||
)}
|
||||
{/* <Badge rounded={"sm"} colorScheme="forestGreen" ms={2}>
|
||||
{IODetails?.ioCashStatusHistory?.Pending.length || 0}
|
||||
</Badge> */}
|
||||
</Tab>
|
||||
<Tab
|
||||
fontSize={"sm"}
|
||||
_selected={{
|
||||
color: "#004118",
|
||||
borderBottom: "2px solid #38a169",
|
||||
}}
|
||||
>
|
||||
Rejected
|
||||
</Tab>
|
||||
</TabList>
|
||||
{IODetails?.isInvestedAmount
|
||||
? isMaker() &&
|
||||
IODetails?.ioSatatus !== "Exited" && (
|
||||
<Button
|
||||
onClick={handleAdd}
|
||||
leftIcon={<AddIcon />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
)
|
||||
: null}
|
||||
</Box>
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<Approved />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<Pending />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<Rejected />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
<AddCaseDetails
|
||||
setActiveTab={setActiveTab}
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
firstField={firstField}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default IOCashDetails;
|
||||
471
src/Pages/IO_Management/CreateIO/IOCashDetails/Pending.jsx
Normal file
@@ -0,0 +1,471 @@
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Table,
|
||||
Tag,
|
||||
Tbody,
|
||||
Text,
|
||||
Th,
|
||||
Tooltip,
|
||||
Tr,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
AddIcon,
|
||||
CheckIcon,
|
||||
CloseIcon,
|
||||
DeleteIcon,
|
||||
EditIcon,
|
||||
ViewIcon,
|
||||
} from "@chakra-ui/icons";
|
||||
import { LuFileSpreadsheet } from "react-icons/lu";
|
||||
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
|
||||
import NormalTable from "../../../../Components/DataTable/NormalTable";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import AddCashDetails from "../AddCashDetails";
|
||||
import { debounce } from "../../../Admin/Contact";
|
||||
import AddPending from "./AddPending";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
|
||||
import RequestApproveModal from "./RequestApproveModal";
|
||||
import RequestRejectModal from "./RequestRejectModal";
|
||||
import AddCaseDetails from "./AddCaseDetails";
|
||||
import { encryptString, isMaker } from "../../../../Constants/Constants";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString();
|
||||
|
||||
const Pending = () => {
|
||||
const toast = useToast();
|
||||
const params = useParams();
|
||||
const id = params?.id;
|
||||
const firstField = useRef();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { IODetails, approved, setApproved } = useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
|
||||
const [updateIOCase] = useUpdateIOCaseMutation();
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
onOpen: onConfirmOpen,
|
||||
onClose: onConfirmClose,
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isRejectOpen,
|
||||
onOpen: onRejectOpen,
|
||||
onClose: onRejectClose,
|
||||
} = useDisclosure();
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
// Table filter
|
||||
const filteredData = IODetails?.ioCashStatusHistory?.Pending?.filter(
|
||||
(item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item?.transactionDate;
|
||||
const searchLower = searchTerm?.toLowerCase();
|
||||
const nameMatches = name?.toLowerCase().includes(searchLower);
|
||||
return nameMatches;
|
||||
}
|
||||
);
|
||||
|
||||
const tableHeadRow = [
|
||||
"Sr No.",
|
||||
"Transaction Date",
|
||||
"Transaction Type",
|
||||
"Amount",
|
||||
"Comments",
|
||||
"Update By",
|
||||
"Update On",
|
||||
...(!isMaker() ? ["Actions"] : []),
|
||||
];
|
||||
|
||||
const extractedArray = filteredData?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr No.": (
|
||||
<Text as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
{index + 1}.
|
||||
</Text>
|
||||
),
|
||||
"Transaction Date": (
|
||||
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
|
||||
{formatDate(item?.transactionDate)}
|
||||
</Text>
|
||||
),
|
||||
"Transaction Type": (
|
||||
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
|
||||
{item?.transactionType}
|
||||
</Text>
|
||||
),
|
||||
Amount: (
|
||||
<Text as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Text>
|
||||
),
|
||||
Comments: (
|
||||
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
{item?.comments}
|
||||
</Text>
|
||||
),
|
||||
"Update By": (
|
||||
<Text
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
{/* <Avatar
|
||||
mr={2}
|
||||
size="sm"
|
||||
name={item?.creator?.firstName}
|
||||
src={item?.creator?.profilePhoto}
|
||||
/> */}
|
||||
{item?.modifier?.firstName}
|
||||
</Text>
|
||||
),
|
||||
"Update On": (
|
||||
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
{formatDate(item.updatedAt)}
|
||||
</Text>
|
||||
),
|
||||
Actions: (
|
||||
<Box display={"flex"} justifyContent={"center"}>
|
||||
{!isMaker() ? (
|
||||
<Box>
|
||||
{index === 0 && (
|
||||
<Box display={"flex"} justifyContent={"center"} gap={2}>
|
||||
<Tooltip
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
label="Approve"
|
||||
bg="#fff"
|
||||
color={"green.500"}
|
||||
placement="left-start"
|
||||
>
|
||||
<Button
|
||||
// colorScheme="forestGreen"
|
||||
// color="green.500"
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={2}
|
||||
py={1}
|
||||
onClick={() => {
|
||||
setActionId(item.id);
|
||||
onConfirmOpen();
|
||||
}}
|
||||
colorScheme="green"
|
||||
variant={"solid"}
|
||||
cursor={"pointer"}
|
||||
>
|
||||
<CheckIcon fontSize={"12px"} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
label="Reject"
|
||||
bg="#fff"
|
||||
color={"red.500"}
|
||||
placement="left-start"
|
||||
>
|
||||
<Button
|
||||
colorScheme="red"
|
||||
// color="red.500"
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={2}
|
||||
onClick={() => {
|
||||
setActionId(item.id);
|
||||
onRejectOpen();
|
||||
}}
|
||||
py={1}
|
||||
// variant={"solid"}
|
||||
>
|
||||
<CloseIcon fontSize={"10px"} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
) : (
|
||||
<Button
|
||||
colorScheme="green"
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
px={2}
|
||||
py={1}
|
||||
fontWeight={500}
|
||||
onClick={() => {
|
||||
setActionId(item.id);
|
||||
}}
|
||||
>
|
||||
<ViewIcon me={"4px"} /> View
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedSponsors = sponser.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setCaseDetails(updatedSponsors);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
const ioCashExporteDetails = IODetails?.ioCashStatusHistory?.Approved?.map(
|
||||
(item, index) => ({
|
||||
"Transaction date": item?.transactionDate,
|
||||
"Transaction type": item?.transactionType,
|
||||
Amount: parseFloat(item?.transactionAmount) || 0,
|
||||
Comments: item?.comments,
|
||||
})
|
||||
);
|
||||
|
||||
const Total = () => {
|
||||
return (
|
||||
<Table size="sm">
|
||||
<Tbody backgroundColor="gray.50">
|
||||
<Tr>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="130px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
Balance in IO Cash
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="150px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
{" "}
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="150px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
{}
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="100px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{parseFloat(IODetails?.ioCash || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="100px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
{" "}
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="100px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
></Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="80px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
></Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="50px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
></Th>
|
||||
</Tr>
|
||||
</Tbody>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
const handleAdd = async () => {
|
||||
try {
|
||||
const res = await updateIOCase(id);
|
||||
if (res?.data) {
|
||||
// toast({
|
||||
// render: () => (
|
||||
// <ToastBox status={"success"} message={res?.data?.message} />
|
||||
// ),
|
||||
// });
|
||||
setIsLoading(false);
|
||||
onOpen();
|
||||
} else if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} pb={0}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
pb={3}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
{/* <HStack display={"flex"} alignItems={"center"}>
|
||||
{IODetails?.isInvestedAmount ? (
|
||||
localStorage?.getItem('role') ==="Maker"&& <Button
|
||||
onClick={handleAdd}
|
||||
leftIcon={<AddIcon />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
) : null}
|
||||
</HStack> */}
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
emptyMessage={`We don't have any Sponers`}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
// total={<Total />}
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
|
||||
<AddCaseDetails
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
firstField={firstField}
|
||||
/>
|
||||
|
||||
<RequestApproveModal
|
||||
// data={data?.data?.rows}
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
id={actionId}
|
||||
// firstField={firstField}
|
||||
/>
|
||||
<RequestRejectModal
|
||||
isOpen={isRejectOpen}
|
||||
onClose={onRejectClose}
|
||||
id={actionId}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Pending;
|
||||
357
src/Pages/IO_Management/CreateIO/IOCashDetails/Rejected.jsx
Normal file
@@ -0,0 +1,357 @@
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Table,
|
||||
Tag,
|
||||
Tbody,
|
||||
Text,
|
||||
Th,
|
||||
Tooltip,
|
||||
Tr,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||
import { AddIcon} from "@chakra-ui/icons";
|
||||
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
|
||||
import NormalTable from "../../../../Components/DataTable/NormalTable";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
|
||||
import AddCashDetails from "../AddCashDetails";
|
||||
import AddRejected from "./AddRejected";
|
||||
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
|
||||
import { useParams } from "react-router-dom";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import AddCaseDetails from "./AddCaseDetails";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString();
|
||||
|
||||
const Rejected = () => {
|
||||
const params = useParams()
|
||||
const id = params?.id
|
||||
const toast = useToast();
|
||||
const firstField = useRef();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { IODetails, approved, setApproved } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
|
||||
const [updateIOCase] = useUpdateIOCaseMutation()
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Table filter
|
||||
const filteredData = IODetails?.ioCashStatusHistory?.Reject?.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item.transactionDate;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
return nameMatches;
|
||||
});
|
||||
|
||||
const tableHeadRow = [
|
||||
"Sr No.",
|
||||
"Transaction Date",
|
||||
"Transaction Type",
|
||||
"Amount",
|
||||
"Comments",
|
||||
"Update By",
|
||||
"Update On",
|
||||
];
|
||||
|
||||
const extractedArray = filteredData?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr No.": (
|
||||
<Text
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{index + 1}.
|
||||
</Text>
|
||||
),
|
||||
"Transaction Date": (
|
||||
<Text
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{formatDate(item?.transactionDate)}
|
||||
</Text>
|
||||
),
|
||||
"Transaction Type": (
|
||||
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
|
||||
{item?.transactionType}
|
||||
</Text>
|
||||
),
|
||||
"Amount": (
|
||||
<Text
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Text>
|
||||
),
|
||||
"Comments": (
|
||||
<Text
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{item?.comments}
|
||||
</Text>
|
||||
),
|
||||
"Update By": (
|
||||
<Text
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
{/* <Avatar
|
||||
mr={2}
|
||||
size="sm"
|
||||
name={item.creator?.firstName}
|
||||
src={item.creator?.profilePhoto}
|
||||
/> */}
|
||||
{item?.modifier?.firstName}
|
||||
</Text>
|
||||
),
|
||||
"Update On": (
|
||||
<Text
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{formatDate(item.updatedAt)}
|
||||
</Text>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedSponsors = sponser.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setCaseDetails(updatedSponsors);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
const Total = () => {
|
||||
return (
|
||||
<Table size="sm">
|
||||
<Tbody backgroundColor="gray.50">
|
||||
<Tr>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="130px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
Balance in IO Cash
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="150px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
{" "}
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="150px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
{}
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="100px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
{"48,000.00"}
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="100px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
>
|
||||
{" "}
|
||||
</Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="100px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
></Th>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
width="100px"
|
||||
color={"#004118"}
|
||||
whiteSpace="normal"
|
||||
wordBreak="normal"
|
||||
overflowWrap="normal"
|
||||
></Th>
|
||||
</Tr>
|
||||
</Tbody>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const handleAdd = async () =>{
|
||||
try {
|
||||
const res = await updateIOCase(id)
|
||||
if (res?.data) {
|
||||
// toast({
|
||||
// render: () => (
|
||||
// <ToastBox status={"success"} message={res?.data?.message} />
|
||||
// ),
|
||||
// });
|
||||
setIsLoading(false);
|
||||
onOpen()
|
||||
|
||||
} else if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} pb={0}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
pb={3}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
{/* <HStack display={"flex"} alignItems={"center"}>
|
||||
{IODetails?.isInvestedAmount ? (
|
||||
localStorage?.getItem('role') ==="Maker"&& <Button
|
||||
onClick={handleAdd}
|
||||
leftIcon={<AddIcon />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
) : null}
|
||||
</HStack> */}
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
emptyMessage={`We don't have any Sponers`}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
// total={<Total/>}
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
|
||||
<AddCaseDetails
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
firstField={firstField}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Rejected;
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import { useApproveIOCaseMutation } from "../../../../Services/io.service";
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
// comments: yup.string().required("Comment is required")
|
||||
// .max(50, "Investment name cannot be more than 50 characters"),
|
||||
comments: yup
|
||||
.string()
|
||||
.required("Comment is required")
|
||||
.max(200, "Approve Comment cannot be more than 200 characters"),
|
||||
});
|
||||
|
||||
const RequestApproveModal = ({ isOpen, onClose, firstField ,id}) => {
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
const [ approveIOCase ] = useApproveIOCaseMutation()
|
||||
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
console.log(data, "tewxttttt");
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await approveIOCase({data,id})
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}else if(res?.data){
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.data?.message} />
|
||||
),
|
||||
});
|
||||
onClose()
|
||||
setIsBtnLoading(false)
|
||||
}else{
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={'error'} message={"Something went wrong"} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const selectedFile = event.target.files[0];
|
||||
setFile(selectedFile);
|
||||
};
|
||||
|
||||
|
||||
const { data, isLoading } =
|
||||
(id, {
|
||||
skip: !id,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
reset({
|
||||
investorAmount: data?.data?.investorAmount,
|
||||
});
|
||||
}
|
||||
}, [data, reset]);
|
||||
|
||||
const heandleOnClose = () =>{
|
||||
reset()
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
|
||||
<ModalOverlay />
|
||||
<ModalContent pb={4}>
|
||||
<ModalHeader fontSize={"md"}>Approve Comment</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
{isLoading ? (
|
||||
<FullscreenLoaders height={"50vh"} />
|
||||
) : (
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">Comment</FormLabel>
|
||||
<Textarea
|
||||
rows={6}
|
||||
focusBorderColor="green.400"
|
||||
name="comments"
|
||||
{...register("comments")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="md"
|
||||
placeholder={"Enter your checker comment...."}
|
||||
rounded={"md"}
|
||||
resize={"none"}
|
||||
maxLength={200}
|
||||
/>
|
||||
{errors.comments && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.comments.message}
|
||||
</Text>
|
||||
)}
|
||||
<FormHelperText fontSize="xs" color="gray.500">
|
||||
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
|
||||
{watch("comments")?.length || 0} characters.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
colorScheme="gray"
|
||||
mr={3}
|
||||
onClick={onClose}
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RequestApproveModal;
|
||||
@@ -0,0 +1,172 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import { useRejectIOCaseMutation } from "../../../../Services/io.service";
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
comments: yup.string().required("Comment is required")
|
||||
.max(200, "Approve Comment cannot be more than 200 characters"),
|
||||
});
|
||||
|
||||
const RequestRejectModal = ({ isOpen, onClose, firstField ,id}) => {
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
const [ rejectIOCase ] = useRejectIOCaseMutation()
|
||||
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
console.log(data, "tewxttttt");
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await rejectIOCase({data,id})
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}else if(res?.data){
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.data?.message} />
|
||||
),
|
||||
});
|
||||
onClose()
|
||||
setIsBtnLoading(false)
|
||||
}else{
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={'error'} message={"Something went wrong"} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const selectedFile = event.target.files[0];
|
||||
setFile(selectedFile);
|
||||
};
|
||||
|
||||
|
||||
const { data, isLoading } =
|
||||
(id, {
|
||||
skip: !id,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
reset({
|
||||
investorAmount: data?.data?.investorAmount,
|
||||
});
|
||||
}
|
||||
}, [data, reset]);
|
||||
|
||||
const heandleOnClose = () =>{
|
||||
reset()
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
|
||||
<ModalOverlay />
|
||||
<ModalContent pb={4}>
|
||||
<ModalHeader fontSize={"md"}>Reject Comment</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
{isLoading ? (
|
||||
<FullscreenLoaders height={"50vh"} />
|
||||
) : (
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">Comment</FormLabel>
|
||||
<Textarea
|
||||
rows={6}
|
||||
focusBorderColor="green.400"
|
||||
name="comments"
|
||||
{...register("comments")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="md"
|
||||
placeholder={"Enter your comments...."}
|
||||
rounded={"md"}
|
||||
resize={"none"}
|
||||
maxLength={200}
|
||||
/>
|
||||
{errors.comments && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.comments.message}
|
||||
</Text>
|
||||
)}
|
||||
<FormHelperText fontSize="xs" color="gray.500">
|
||||
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
|
||||
{watch("comments")?.length || 0} characters.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
colorScheme="gray"
|
||||
mr={3}
|
||||
onClick={onClose}
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RequestRejectModal;
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
@@ -23,14 +24,17 @@ import ToastBox from "../../../Components/ToastBox";
|
||||
import { debounce } from "../../Master/Sponser/AddSponser";
|
||||
import { AddIcon } from "@chakra-ui/icons";
|
||||
import AddCashDetails from "./AddCashDetails";
|
||||
import { LuFileSpreadsheet } from "react-icons/lu";
|
||||
import { exportToExcel, exportToExcelNew } from "../../../Constants/Constants";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
|
||||
|
||||
const IOCashDetails = () => {
|
||||
const IOCashDetailsOld = () => {
|
||||
const toast = useToast();
|
||||
const firstField = useRef();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { caseDetails, setCaseDetails, IODetails } = useContext(GlobalStateContext);
|
||||
const { caseDetails, setCaseDetails, IODetails } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
@@ -48,6 +52,14 @@ const IOCashDetails = () => {
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
// Calculate totals
|
||||
const totalAmount = caseDetails.reduce(
|
||||
(acc, caseDetail) => acc + caseDetail.amount,
|
||||
@@ -76,17 +88,18 @@ const IOCashDetails = () => {
|
||||
}, 300);
|
||||
|
||||
// Table filter
|
||||
const filteredData = IODetails?.ioCashHistory?.filter((item) => {
|
||||
const name = item.transactionType;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
return nameMatches;
|
||||
}).sort((b, a) => new Date(a.createdAt) - new Date(b.createdAt));
|
||||
|
||||
const filteredData = IODetails?.ioCashHistory
|
||||
?.filter((item) => {
|
||||
const name = item.transactionType;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
return nameMatches;
|
||||
})
|
||||
.sort((b, a) => new Date(a.createdAt) - new Date(b.createdAt));
|
||||
|
||||
const extractedArray = filteredData?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Date": (
|
||||
Date: (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
@@ -94,12 +107,12 @@ const IOCashDetails = () => {
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item?.transactionDate}
|
||||
{formatDate(item?.transactionDate)}
|
||||
</Text>
|
||||
),
|
||||
"Transaction type": (
|
||||
<Text
|
||||
justifyContent ={"center"}
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
@@ -108,20 +121,27 @@ const IOCashDetails = () => {
|
||||
{item?.transactionType}
|
||||
</Text>
|
||||
),
|
||||
"Amount": (
|
||||
Amount: (
|
||||
<Text
|
||||
justifyContent ={"center"}
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{`$${parseFloat(item.transactionAmount||0).toLocaleString()}`}
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{/* {parseFloat(item.transactionAmount || 0).toLocaleString()} */}
|
||||
{`${parseFloat(item.transactionAmount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}`}
|
||||
</Text>
|
||||
),
|
||||
"Comments": (
|
||||
Comments: (
|
||||
<Text
|
||||
justifyContent ={"center"}
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
@@ -132,30 +152,53 @@ const IOCashDetails = () => {
|
||||
),
|
||||
"Update by ": (
|
||||
<Text
|
||||
justifyContent ={"center"}
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
gap={2}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
|
||||
<Avatar size='sm' name={item.creator?.firstName} src={item.creator?.profilePhoto} />{item.creator?.firstName}
|
||||
<Avatar
|
||||
size="sm"
|
||||
name={item.creator?.firstName}
|
||||
src={item.creator?.profilePhoto}
|
||||
/>
|
||||
{item.creator?.firstName}
|
||||
</Text>
|
||||
),
|
||||
"Update On": (
|
||||
<Text
|
||||
justifyContent ={"center"}
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.updateOn}
|
||||
{formatDate(item.updatedAt)}
|
||||
</Text>
|
||||
),
|
||||
}));
|
||||
|
||||
const customHeaders = [
|
||||
{ label: "Date", key: "transactionDate" },
|
||||
{ label: "Transaction type", key: "transactionType" },
|
||||
{ label: "Amount", key: "transactionAmount" },
|
||||
{ label: "Comments", key: "comments" },
|
||||
// { label: "Update by", key: "creator" },
|
||||
// { label: "Update On", key: "updateOn" },
|
||||
// Add more headers as needed
|
||||
];
|
||||
|
||||
const ioCashExporteDetails = IODetails?.ioCashHistory?.map((item, index) => ({
|
||||
Date: item?.transactionDate,
|
||||
"Transaction type": item?.transactionType,
|
||||
Amount: parseFloat(item?.transactionAmount) || 0,
|
||||
Comments: item?.comments,
|
||||
}));
|
||||
|
||||
console.log(ioCashExporteDetails);
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedSponsors = sponser.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
@@ -173,7 +216,7 @@ const IOCashDetails = () => {
|
||||
return (
|
||||
<Table size="sm">
|
||||
<Tbody backgroundColor="gray.50">
|
||||
<Tr >
|
||||
<Tr>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
@@ -267,9 +310,35 @@ const IOCashDetails = () => {
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<HStack display={"flex"} alignItems={"center"}>
|
||||
<Button
|
||||
onClick={() =>
|
||||
exportToExcelNew(ioCashExporteDetails, "IO Cash History")
|
||||
}
|
||||
leftIcon={<LuFileSpreadsheet />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
variant={"outline"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
isDisabled={ioCashExporteDetails?.length === 0}
|
||||
>
|
||||
Export xls
|
||||
</Button>
|
||||
|
||||
{IODetails?.isInvestedAmount ? <Button onClick={onOpen} leftIcon={<AddIcon/>} colorScheme="forestGreen" size={'sm'} rounded={'sm'} fontSize={'xs'} >Add IO Cash</Button>:null}
|
||||
|
||||
{IODetails?.isInvestedAmount ? (
|
||||
<Button
|
||||
onClick={onOpen}
|
||||
leftIcon={<AddIcon />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
>
|
||||
Add IO Cash
|
||||
</Button>
|
||||
) : null}
|
||||
</HStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
@@ -277,7 +346,7 @@ const IOCashDetails = () => {
|
||||
centered={true}
|
||||
emptyMessage={`We don't have any Sponers`}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
@@ -294,25 +363,13 @@ const IOCashDetails = () => {
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<AddCashDetails
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
firstField={firstField} />
|
||||
|
||||
|
||||
|
||||
|
||||
<AddCashDetails
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
firstField={firstField}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default IOCashDetails;
|
||||
export default IOCashDetailsOld;
|
||||
@@ -6,35 +6,18 @@ import { useForm } from "react-hook-form";
|
||||
import * as yup from "yup";
|
||||
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
|
||||
import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
|
||||
import { generateUniqueId } from "../../../Contexts/GlobalStateProvider";
|
||||
import { useGetInvestmentTypesQuery } from "../../../Services/investment.type.service";
|
||||
import { useGetActiveSponserMasterQuery } from "../../../Services/sponser.service";
|
||||
import {
|
||||
useCreateIOMutation,
|
||||
useGetIOByIdQuery,
|
||||
useUpdateIOMutation,
|
||||
} from "../../../Services/io.service";
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
import {
|
||||
Input,
|
||||
Table,
|
||||
Tbody,
|
||||
Td,
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import { formatDate } from "../../Master/Sponser/Sponsers";
|
||||
|
||||
import behrain from "../../../assets/bahrain_flag.png";
|
||||
import kuwait from "../../../assets/kuwait_flag.png";
|
||||
import oman from "../../../assets/oman_flag.png";
|
||||
import qatar from "../../../assets/qatar_flag.png";
|
||||
import uae from "../../../assets/uae_flag.png";
|
||||
import saudi from "../../../assets/saudi_arabia_flag.png";
|
||||
import { useToast } from "@chakra-ui/react";
|
||||
import { formatDatee } from "../../../Components/FormField";
|
||||
import { removeTrailingZeros } from "../../../Constants/Constants";
|
||||
import {
|
||||
formatDateToYYYYMMDD,
|
||||
removeTrailingZeros,
|
||||
} from "../../../Constants/Constants";
|
||||
|
||||
const schema = yup.object().shape({
|
||||
investmentNameEnglish: yup
|
||||
@@ -60,16 +43,15 @@ const schema = yup.object().shape({
|
||||
.required("Description in Arabic is required")
|
||||
.min(10, "Description in Arabic must be at least 10 characters long")
|
||||
.max(2000, "Description in Arabic must be at most 500 characters long"),
|
||||
expectedReturnArabic: yup
|
||||
.string()
|
||||
.required("Expected return in Arabic is required"),
|
||||
|
||||
goalAmount: yup
|
||||
.number()
|
||||
.typeError("Goal Amount is must be number")
|
||||
.required('Goal amount is required')
|
||||
.positive('Goal amount must be a positive number'),
|
||||
expectedReturnArabic: yup
|
||||
.string()
|
||||
.required("Expected return in Arabic is required"),
|
||||
|
||||
goalAmount: yup
|
||||
.number()
|
||||
.typeError("Goal Amount is must be number")
|
||||
.required("Goal amount is required")
|
||||
.positive("Goal amount must be a positive number"),
|
||||
closingDate: yup
|
||||
.date()
|
||||
.notRequired("Closing date is required")
|
||||
@@ -88,31 +70,35 @@ const schema = yup.object().shape({
|
||||
|
||||
InvestmentDetails: yup.string().notRequired(),
|
||||
|
||||
comment: yup.string().notRequired()
|
||||
.min(10, "Comment must be at least 10 characters long")
|
||||
.max(100, "Comment must be at most 100 characters long"),
|
||||
|
||||
expectedReturn: yup
|
||||
comment: yup
|
||||
.string()
|
||||
.required("Expected return is required"),
|
||||
.notRequired()
|
||||
// .min(10, "Comment must be at least 10 characters long")
|
||||
.max(100, "Comment must be at most 100 characters long"),
|
||||
|
||||
expectedReturn: yup.string().required("Expected return is required"),
|
||||
});
|
||||
|
||||
|
||||
const IODetails = ({ enableNextTab, index, data }) => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const params = useParams();
|
||||
const navigate = useNavigate();
|
||||
const toast = useToast();
|
||||
|
||||
|
||||
const handleInputChange = (index, newValue) => {
|
||||
const handleInputChangeCreate = (index, newValue) => {
|
||||
const updatedValues = [...values];
|
||||
updatedValues[index].value = newValue;
|
||||
setValues(updatedValues);
|
||||
console.log(values);
|
||||
};
|
||||
|
||||
const handleInputChangeEdit = (index, newValue) => {
|
||||
// Allow only whole numbers using regex
|
||||
if (/^\d*$/.test(newValue)) {
|
||||
const updatedValues = [...values];
|
||||
updatedValues[index].value = newValue;
|
||||
setValues(updatedValues);
|
||||
console.log(values);
|
||||
}
|
||||
};
|
||||
|
||||
// ======================[ States ]
|
||||
@@ -132,6 +118,7 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
isLoading: IObyIDisLoading,
|
||||
error: IObyIDerror,
|
||||
} = useGetIOByIdQuery(id, { skip: !id });
|
||||
console.log(IObyID);
|
||||
|
||||
const [creatIO] = useCreateIOMutation();
|
||||
const [updateIO] = useUpdateIOMutation();
|
||||
@@ -154,9 +141,12 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
});
|
||||
|
||||
const miniValue = data?.country?.map(
|
||||
({ countryName, flagIcon, minInvestmentAmt, countryCode, id, currency }, index) => {
|
||||
(
|
||||
{ countryName, flagIcon, minInvestmentAmt, countryCode, id, currency },
|
||||
index
|
||||
) => {
|
||||
return {
|
||||
id:id,
|
||||
id: id,
|
||||
country: countryName,
|
||||
value: minInvestmentAmt,
|
||||
logo: flagIcon,
|
||||
@@ -165,24 +155,87 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
const minInvestmentById = IObyID?.data?.minInvestmentAmt?.map(({minInvestmentAmt, country, country_xid, })=>{
|
||||
return{
|
||||
id:country_xid,
|
||||
country: country?.countryName,
|
||||
value: removeTrailingZeros(minInvestmentAmt),
|
||||
logo: country?.flagIcon,
|
||||
curr: country?.countryCode,
|
||||
const minInvestmentById = IObyID?.data?.minInvestmentAmt?.map(
|
||||
({ minInvestmentAmt, country, currencyCode, country_xid, id }) => {
|
||||
console.log(currencyCode);
|
||||
return {
|
||||
_id: id,
|
||||
id: country_xid,
|
||||
country: country?.countryName,
|
||||
value: removeTrailingZeros(minInvestmentAmt),
|
||||
logo: country?.flagIcon,
|
||||
curr: currencyCode,
|
||||
};
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const schemaEdit = yup.object().shape({
|
||||
investmentNameEnglish: yup
|
||||
.string()
|
||||
.required("IO name in English is required")
|
||||
.min(3, "IO name in English must be at least 3 characters long")
|
||||
.max(150, "IO name in English must be at most 150 characters long"),
|
||||
|
||||
investmentNameArabic: yup
|
||||
.string()
|
||||
.required("IO name in Arabic is required")
|
||||
.min(3, "IO name in Arabic must be at least 3 characters long")
|
||||
.max(50, "IO name in Arabic must be at most 50 characters long"),
|
||||
|
||||
descriptionEnglish: yup
|
||||
.string()
|
||||
.required("Description in English is required")
|
||||
.min(10, "Description in English must be at least 10 characters long")
|
||||
.max(1000, "Description in English must be at most 1000 characters long"),
|
||||
|
||||
descriptionArabic: yup
|
||||
.string()
|
||||
.required("Description in Arabic is required")
|
||||
.min(10, "Description in Arabic must be at least 10 characters long")
|
||||
.max(2000, "Description in Arabic must be at most 500 characters long"),
|
||||
expectedReturnArabic: yup
|
||||
.string()
|
||||
.required("Expected return in Arabic is required"),
|
||||
|
||||
goalAmount: yup
|
||||
.number()
|
||||
.typeError("Goal Amount is must be number")
|
||||
.required("Goal amount is required")
|
||||
.positive("Goal amount must be a positive number")
|
||||
.min(
|
||||
IObyID?.data?.totalAmtInvestmentInUSD,
|
||||
`Goal amount should not be lesser then amount raised ${IObyID?.data?.totalAmtInvestmentInUSD}`
|
||||
),
|
||||
closingDate: yup.date().notRequired("Closing date is required"),
|
||||
|
||||
holdingPeriod: yup.string().required("Holding period is required"),
|
||||
holdingPeriodArabic: yup.string().required("Holding period is required"),
|
||||
isShariah: yup.string().required("CheckBox is required"),
|
||||
|
||||
// minInvestmentAmount: yup
|
||||
// .number()
|
||||
// .required("Minimum investment is required")
|
||||
// .positive("Minimum investment must be a positive number")
|
||||
// .min(1, "Minimum investment must be at least 1"),
|
||||
|
||||
ISIN: yup.string().notRequired(),
|
||||
|
||||
InvestmentDetails: yup.string().notRequired(),
|
||||
|
||||
comment: yup
|
||||
.string()
|
||||
.notRequired()
|
||||
.min(10, "Comment must be at least 10 characters long")
|
||||
.max(100, "Comment must be at most 100 characters long"),
|
||||
|
||||
expectedReturn: yup.string().required("Expected return is required"),
|
||||
});
|
||||
|
||||
const [values, setValues] = useState(id ? minInvestmentById : miniValue);
|
||||
|
||||
|
||||
const [values, setValues] = useState(id?minInvestmentById:miniValue);
|
||||
const formatNumber = (num) => {
|
||||
// Remove non-numeric characters and format with commas
|
||||
return num.replace(/\D/g, '')
|
||||
.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
return num.replace(/\D/g, "").replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
};
|
||||
// console.log(values);
|
||||
|
||||
@@ -195,11 +248,12 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(schema),
|
||||
resolver: yupResolver(id ? schemaEdit : schema),
|
||||
mode: "all",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setIOloading(IObyIDisLoading)
|
||||
setIOloading(IObyIDisLoading);
|
||||
setIODetails({
|
||||
...IObyID?.data,
|
||||
});
|
||||
@@ -220,17 +274,11 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
minInvestmentAmount: IObyID?.data?.minInvestmentAmount,
|
||||
holdingPeriodArabic: IObyID?.data?.minInvestmentAmount,
|
||||
expectedReturnArabic: IObyID?.data?.minInvestmentAmount,
|
||||
isShariah: IObyID?.data?.isShariah,
|
||||
});
|
||||
}
|
||||
|
||||
}, [id, IObyID]);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// const minInvestmentById =
|
||||
|
||||
//=======================[ Creator ]
|
||||
const formFields = [
|
||||
{
|
||||
@@ -241,8 +289,10 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
isRequired: true,
|
||||
section: " ",
|
||||
width: "49%",
|
||||
maxLength:150,
|
||||
helperText:`Maximum length should be 150 characters. You have entered ${watch()?.investmentNameEnglish?.length || 0} characters.`
|
||||
maxLength: 150,
|
||||
helperText: `Maximum length should be 150 characters. You have entered ${
|
||||
watch()?.investmentNameEnglish?.length || 0
|
||||
} characters.`,
|
||||
},
|
||||
{
|
||||
label: "IO Name (Arabic)",
|
||||
@@ -253,8 +303,10 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
arabic: true,
|
||||
section: " ",
|
||||
width: "49%",
|
||||
maxLength:150,
|
||||
helperText:`Maximum length should be 150 characters. You have entered ${watch()?.investmentNameArabic?.length || 0} characters.`
|
||||
maxLength: 150,
|
||||
helperText: `Maximum length should be 150 characters. You have entered ${
|
||||
watch()?.investmentNameArabic?.length || 0
|
||||
} characters.`,
|
||||
},
|
||||
{
|
||||
label: "Description",
|
||||
@@ -264,8 +316,10 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
isRequired: true,
|
||||
section: " ",
|
||||
width: "49%",
|
||||
maxLength:1000,
|
||||
helperText:`Maximum length should be 1000 characters. You have entered ${watch()?.descriptionEnglish?.length || 0} characters.`
|
||||
maxLength: 1000,
|
||||
helperText: `Maximum length should be 1000 characters. You have entered ${
|
||||
watch()?.descriptionEnglish?.length || 0
|
||||
} characters.`,
|
||||
},
|
||||
{
|
||||
label: "Description (Arabic)",
|
||||
@@ -276,9 +330,10 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
arabic: true,
|
||||
section: " ",
|
||||
width: "49%",
|
||||
maxLength:1000,
|
||||
helperText:`Maximum length should be 1000 characters. You have entered ${watch()?.descriptionArabic?.length || 0} characters.`
|
||||
|
||||
maxLength: 1000,
|
||||
helperText: `Maximum length should be 1000 characters. You have entered ${
|
||||
watch()?.descriptionArabic?.length || 0
|
||||
} characters.`,
|
||||
},
|
||||
{
|
||||
label: "Holding Period",
|
||||
@@ -289,8 +344,10 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
section: " ",
|
||||
width: "49%",
|
||||
value: IObyID?.data?.holdingPeriod,
|
||||
maxLength:20,
|
||||
helperText:`Maximum length should be 20 characters. You have entered ${watch()?.holdingPeriod?.length || 0} characters.`
|
||||
maxLength: 20,
|
||||
helperText: `Maximum length should be 20 characters. You have entered ${
|
||||
watch()?.holdingPeriod?.length || 0
|
||||
} characters.`,
|
||||
},
|
||||
{
|
||||
label: "Holding Period (Arabic)",
|
||||
@@ -302,18 +359,19 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
section: " ",
|
||||
width: "49%",
|
||||
value: IObyID?.data?.holdingPeriodArabic,
|
||||
maxLength:20,
|
||||
helperText:`Maximum length should be 20 characters. You have entered ${watch()?.holdingPeriodArabic?.length || 0} characters.`
|
||||
maxLength: 20,
|
||||
helperText: `Maximum length should be 20 characters. You have entered ${
|
||||
watch()?.holdingPeriodArabic?.length || 0
|
||||
} characters.`,
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
label: "Expected Return",
|
||||
name: "expectedReturn",
|
||||
type: "text",
|
||||
isRequired: true,
|
||||
section: " ",
|
||||
width: "49%",
|
||||
width: "32.3%",
|
||||
value: IObyID?.data?.expectedReturn,
|
||||
},
|
||||
|
||||
@@ -324,10 +382,19 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
isRequired: true,
|
||||
arabic: true,
|
||||
section: " ",
|
||||
width: "49%",
|
||||
width: "32.3%",
|
||||
value: IObyID?.data?.expectedReturnArabic,
|
||||
},
|
||||
|
||||
{
|
||||
label: "Shariah",
|
||||
name: "isShariah",
|
||||
type: "checkBox",
|
||||
value: IObyID?.data?.isShariah,
|
||||
// isRequired: true,
|
||||
section: " ",
|
||||
width: "32.3%",
|
||||
value: IObyID?.data?.isShariah,
|
||||
},
|
||||
|
||||
{
|
||||
label: "Investment Type",
|
||||
@@ -366,18 +433,19 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
name: "closingDate",
|
||||
// value: "IObyID?.data?.closingDate",
|
||||
type: "date",
|
||||
isRequired: true,
|
||||
// isRequired: true,
|
||||
section: " ",
|
||||
width: "32.3%",
|
||||
dateValue:formatDatee(IObyID?.data?.closingDate),
|
||||
dateValue: formatDatee(IObyID?.data?.closingDate),
|
||||
// helperText: IObyID && `Current closing date is : ${formatDate(IObyID?.data?.closingDate)}`
|
||||
closingDate:id ? null : true
|
||||
},
|
||||
{
|
||||
label: "ISIN",
|
||||
placeHolder: "",
|
||||
name: "ISIN",
|
||||
type: "text",
|
||||
align:"right",
|
||||
align: "right",
|
||||
section: " ",
|
||||
width: "32.3%",
|
||||
},
|
||||
@@ -389,6 +457,10 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
section: " ",
|
||||
width: "32.3%",
|
||||
value: IObyID?.data?.InvestmentDetails,
|
||||
maxLength: 20,
|
||||
helperText: `Maximum length should be 20 characters. You have entered ${
|
||||
watch()?.InvestmentDetails?.length || 0
|
||||
} characters.`,
|
||||
},
|
||||
|
||||
{
|
||||
@@ -400,7 +472,7 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
width: "100%",
|
||||
isRequired: true,
|
||||
options: investmentTypeOptions,
|
||||
handleInputChange: handleInputChange,
|
||||
handleInputChange: id ? handleInputChangeEdit : handleInputChangeCreate,
|
||||
value: values,
|
||||
},
|
||||
|
||||
@@ -413,155 +485,12 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
width: "100%",
|
||||
options: investmentTypeOptions,
|
||||
value: IObyID?.data?.comment,
|
||||
maxLength:100,
|
||||
helperText:`Maximum length should be 100 characters. You have entered ${watch()?.comment?.length || 0} characters.`
|
||||
maxLength: 100,
|
||||
helperText: `Maximum length should be 100 characters. You have entered ${
|
||||
watch()?.comment?.length || 0
|
||||
} characters.`,
|
||||
},
|
||||
];
|
||||
//=======================[ Editor ]
|
||||
// const formEditFields = [
|
||||
// {
|
||||
// label: "IO Name",
|
||||
// value: IObyID?.data?.investmentNameEnglish,
|
||||
// name: "investmentNameEnglish",
|
||||
// type: "text",
|
||||
// section: " ",
|
||||
// width: "49%",
|
||||
// isRequired: true,
|
||||
// },
|
||||
// {
|
||||
// label: "IO Name (Arabic)",
|
||||
// name: "investmentNameArabic",
|
||||
// type: "text",
|
||||
// value: IObyID?.data?.investmentNameArabic,
|
||||
// isRequired: true,
|
||||
// arabic: true,
|
||||
// section: " ",
|
||||
// width: "49%",
|
||||
// },
|
||||
// {
|
||||
// label: "Description",
|
||||
// name: "descriptionEnglish",
|
||||
// value: IObyID?.data?.descriptionEnglish,
|
||||
// type: "textarea",
|
||||
// isRequired: true,
|
||||
// section: " ",
|
||||
// width: "49%",
|
||||
// },
|
||||
// {
|
||||
// label: "Description (Arabic)",
|
||||
// name: "descriptionArabic",
|
||||
// value: IObyID?.data?.descriptionArabic,
|
||||
// type: "textarea",
|
||||
// isRequired: true,
|
||||
// arabic: true,
|
||||
// section: " ",
|
||||
// width: "49%",
|
||||
// },
|
||||
|
||||
// {
|
||||
// label: "Goal Amount",
|
||||
// placeHolder: "$00.00",
|
||||
// value: IObyID?.data?.goalAmount,
|
||||
// name: "goalAmount",
|
||||
// type: "number",
|
||||
// isRequired: true,
|
||||
// section: " ",
|
||||
// width: "32.3%",
|
||||
// },
|
||||
// {
|
||||
// label: "Closing Date",
|
||||
// name: "closingDate",
|
||||
// type: "date",
|
||||
// isRequired: true,
|
||||
// value: IObyID?.data?.closingDate,
|
||||
// section: " ",
|
||||
// width: "32.3%",
|
||||
// },
|
||||
// {
|
||||
// label: "Holding Period",
|
||||
// name: "holdingPeriod",
|
||||
// value: IObyID?.data?.holdingPeriod,
|
||||
// type: "number",
|
||||
// isRequired: true,
|
||||
// placeHolder: "1Y",
|
||||
// section: " ",
|
||||
// width: "32.3%",
|
||||
// },
|
||||
// {
|
||||
// label: "Minimum Investment Amount",
|
||||
// placeHolder: "$00.00",
|
||||
// name: "minInvestmentAmount",
|
||||
// value: IObyID?.data?.minInvestmentAmount,
|
||||
// type: "number",
|
||||
// isRequired: true,
|
||||
// section: " ",
|
||||
// width: "32.3%",
|
||||
// },
|
||||
// {
|
||||
// label: "ISIN",
|
||||
// placeHolder: "$00.00",
|
||||
// name: "ISIN",
|
||||
// value: IObyID?.data?.ISIN,
|
||||
// type: "number",
|
||||
// section: " ",
|
||||
// width: "32.3%",
|
||||
// },
|
||||
// {
|
||||
// label: "Investment Details",
|
||||
// placeHolder: "",
|
||||
// name: "InvestmentDetails",
|
||||
// value: IObyID?.data?.InvestmentDetails,
|
||||
// type: "text",
|
||||
// section: " ",
|
||||
// width: "32.3%",
|
||||
// },
|
||||
// {
|
||||
// label: "Expected Return Estimated",
|
||||
// placeHolder: "$00.00",
|
||||
// name: "expectedReturn",
|
||||
// type: "number",
|
||||
// isRequired: true,
|
||||
// value: IObyID?.data?.expectedReturn,
|
||||
// section: " ",
|
||||
// width: "32.3%",
|
||||
// },
|
||||
|
||||
// {
|
||||
// label: "Investment Type",
|
||||
// placeHolder: "Select option",
|
||||
// value: IObyID?.data?.investmentType_xid,
|
||||
// name: "investmentType_xid",
|
||||
// type: "select",
|
||||
// isRequired: true,
|
||||
// section: " ",
|
||||
// width: "32.3%",
|
||||
// options: investmentTypeOptions,
|
||||
// },
|
||||
// {
|
||||
// label: "Sponsorer Name",
|
||||
// placeHolder: "Select option",
|
||||
// name: "sponsor_xid",
|
||||
// type: "select",
|
||||
// options: sponserNameOption,
|
||||
// value: IObyID?.data?.sponsor_xid,
|
||||
// section: " ",
|
||||
// isRequired: true,
|
||||
// width: "32.3%",
|
||||
// },
|
||||
|
||||
// {
|
||||
// label: "Comment",
|
||||
// placeHolder: "Enter comment here",
|
||||
// name: "comment",
|
||||
// type: "textarea",
|
||||
// value: IObyID?.data?.comment,
|
||||
// section: " ",
|
||||
// width: "100%",
|
||||
// // options: investmentTypeOptions,
|
||||
// },
|
||||
// ];
|
||||
|
||||
// ======================[ Form Contructor Filter ]
|
||||
const groupedFields = formFields.reduce((groups, field) => {
|
||||
const { section } = field;
|
||||
if (!groups[section]) {
|
||||
@@ -573,26 +502,28 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
delete data.table;
|
||||
|
||||
setIsLoading(true);
|
||||
// console.log(data);
|
||||
|
||||
const updatedMinAmount = values?.map(({id, value})=>{
|
||||
const updatedMinAmount = values?.map(({ id, value, _id }) => {
|
||||
return {
|
||||
country_xid:id,
|
||||
minInvestmentAmt: Number(value)
|
||||
}
|
||||
})
|
||||
id: _id,
|
||||
country_xid: id,
|
||||
minInvestmentAmt: Number(value),
|
||||
};
|
||||
});
|
||||
|
||||
// console.log(formatDateToYYYYMMDD(data.closingDate));
|
||||
const formData = {
|
||||
...data,
|
||||
investmentType_xid: Number(data.investmentType),
|
||||
sponsor_xid: Number(data.sponserName),
|
||||
minInvestmentAmt:updatedMinAmount
|
||||
minInvestmentAmt: updatedMinAmount,
|
||||
closingDate: formatDateToYYYYMMDD(data.closingDate),
|
||||
};
|
||||
|
||||
// console.log(formData);
|
||||
// console.log(formData);
|
||||
if (id) {
|
||||
console.log("========================", formData);
|
||||
const res = await updateIO({ data: formData, id });
|
||||
console.log(res);
|
||||
if (res?.data?.statusCode === 200) {
|
||||
@@ -602,14 +533,24 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
});
|
||||
navigate(`/view-io/${id}`);
|
||||
enableNextTab(index);
|
||||
} else if(res?.error?.status === 400){
|
||||
} else if (res?.error?.status === 400) {
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
|
||||
render: () => (
|
||||
<ToastBox message={res?.error?.data?.message} status={"error"} />
|
||||
),
|
||||
});
|
||||
} else if (res?.error?.status === 500) {
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.error?.data?.message} status={"error"} />
|
||||
),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
console.log("========================", formData);
|
||||
const res = await creatIO(formData);
|
||||
console.log(res?.error?.status);
|
||||
if (res?.data?.statusCode === 200) {
|
||||
@@ -619,15 +560,19 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
});
|
||||
navigate(`/view-io/${res?.data?.data}`);
|
||||
enableNextTab(index);
|
||||
} else if(res?.error?.status === 400){
|
||||
} else if (res?.error?.status === 400) {
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
|
||||
render: () => (
|
||||
<ToastBox message={res?.error?.data?.message} status={"error"} />
|
||||
),
|
||||
});
|
||||
}else if(res?.error?.status === 500){
|
||||
} else if (res?.error?.status === 500) {
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
|
||||
render: () => (
|
||||
<ToastBox message={res?.error?.data?.message} status={"error"} />
|
||||
),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -636,7 +581,6 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ==========================
|
||||
// if (params?.id) {
|
||||
// return enableNextTab(index);
|
||||
@@ -654,9 +598,8 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
};
|
||||
|
||||
return IObyIDisLoading ? (
|
||||
<FullscreenLoaders height={'70vh'} />
|
||||
<FullscreenLoaders height={"70vh"} />
|
||||
) : (
|
||||
|
||||
<FormInputMain
|
||||
p={0.1}
|
||||
w={250}
|
||||
@@ -667,7 +610,13 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
btnLoading={isLoading}
|
||||
submitTitle={id ? "Update" : "Submit"}
|
||||
></FormInputMain>
|
||||
>
|
||||
{/* <Box display={"flex"} justifyContent={"end"} mb={3} mt={4} me={3}>
|
||||
<Checkbox colorScheme='forestGreen' display={"flex"} gap={3} flexDirection= {"row-reverse"}>
|
||||
<Text as={"span"} fontWeight={500} fontSize={"sm"}>Shariah</Text>
|
||||
</Checkbox>
|
||||
</Box> */}
|
||||
</FormInputMain>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react'
|
||||
import GlobalStateContext from '../../../Contexts/GlobalStateContext';
|
||||
import { Box, HStack, Input,Text, Table, Tbody, Th, Tr, Avatar, useDisclosure,Button } from '@chakra-ui/react';
|
||||
import { OPACITY_ON_LOAD } from '../../../Layout/animations';
|
||||
import Pagination from '../../../Components/Pagination';
|
||||
import NormalTable from '../../../Components/DataTable/NormalTable';
|
||||
import CustomAlertDialog from '../../../Components/CustomAlertDialog';
|
||||
import { formatDatee } from '../../../Components/FormField';
|
||||
import { AddIcon } from '@chakra-ui/icons';
|
||||
import AddIONav from './AddIONav';
|
||||
|
||||
const IONAVDetails = () => {
|
||||
const { navDetails, setNavDetails, IODetails } =
|
||||
useContext(GlobalStateContext);
|
||||
const firstField = useRef();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
|
||||
|
||||
console.log(IODetails?.ioNAVHistory);
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
// Table setup
|
||||
const tableHeadRow = [
|
||||
// "Sr.No",
|
||||
"Valuation Date",
|
||||
"NAV",
|
||||
"Last NAV update",
|
||||
"Investment Closed",
|
||||
"Comments",
|
||||
"Update by ",
|
||||
// "Update On",
|
||||
];
|
||||
|
||||
// Table filter
|
||||
const filteredData = IODetails?.ioNAVHistory?.filter((item) => {
|
||||
const name = item.transactionType;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
return nameMatches;
|
||||
}).sort((b, a) => new Date(a.transactionDate) - new Date(b.transactionDate));
|
||||
|
||||
const extractedArray=filteredData?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr.No": index +1,
|
||||
"Valuation Date": (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{formatDatee(item.transactionDate)}
|
||||
</Text>
|
||||
),
|
||||
"NAV": (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{/* {`${item.transactionAmount}`} */}
|
||||
|
||||
{`$${parseFloat(item.transactionAmount||0).toLocaleString()}`}
|
||||
</Text>
|
||||
),
|
||||
"Last NAV update": (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.previousNAVvalue && `${item.previousNAVvalue}`}
|
||||
</Text>
|
||||
),
|
||||
"Investment Closed": (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.initialNAVvalue&& `${item.initialNAVvalue}`}
|
||||
</Text>
|
||||
),
|
||||
"Comments": (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.comments}
|
||||
</Text>
|
||||
),
|
||||
"Update by ": (
|
||||
<Text
|
||||
justifyContent ={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
gap={2}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
|
||||
<Avatar size='sm' name={"faisal"} src={null} />Faisal
|
||||
</Text>
|
||||
),
|
||||
"Update On": (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.updateOn}
|
||||
</Text>
|
||||
),
|
||||
}));
|
||||
|
||||
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedNav = navDetails.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setNavDetails(updatedNav);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
return (<Box {...OPACITY_ON_LOAD} pb={0}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
pb={3}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
{/* <HStack display={"flex"} alignItems={"center"}>
|
||||
<Pagination totalItems={10} />
|
||||
</HStack> */}
|
||||
|
||||
|
||||
{IODetails?.isInvestedAmount ? <Button onClick={onOpen} leftIcon={<AddIcon/>} colorScheme="forestGreen" size={'sm'} rounded={'sm'} fontSize={'xs'} >Add IO NAV</Button>:null}
|
||||
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
centered={true}
|
||||
emptyMessage={`We don't have any Sponers`}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
caption={"Tanami v1.0"}
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
|
||||
|
||||
|
||||
<AddIONav
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
firstField={firstField} />
|
||||
|
||||
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default IONAVDetails
|
||||
266
src/Pages/IO_Management/CreateIO/IONAVDetails/AddNavDetails.jsx
Normal file
@@ -0,0 +1,266 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Drawer,
|
||||
DrawerBody,
|
||||
DrawerCloseButton,
|
||||
DrawerContent,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerOverlay,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
HStack,
|
||||
Input,
|
||||
Select,
|
||||
Stack,
|
||||
Text,
|
||||
Textarea,
|
||||
VStack,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import * as yup from "yup";
|
||||
import React, { useState, useEffect, useContext } from "react";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { useParams } from "react-router-dom";
|
||||
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import CurrencyInput from "../../../../Components/CurrencyInput";
|
||||
import { useAddNavDetailsMutation } from "../../../../Services/io.service";
|
||||
import { formatDatee } from "../../../../Components/FormField";
|
||||
|
||||
const ioNav = yup.object().shape({
|
||||
transactionDate: yup.string().required("Date is required"),
|
||||
transactionAmount: yup.number().required("New NAV is required"),
|
||||
comments: yup.string().notRequired()
|
||||
.max(200, "Approve Comment cannot be more than 200 characters"),
|
||||
});
|
||||
|
||||
const AddNavDetails = ({ isOpen, onClose, firstField, actionId, setActionId, data }) => {
|
||||
const params = useParams()
|
||||
const id = params?.id
|
||||
const [file, setFile] = useState("");
|
||||
const [fileName, setFileName] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [alert, setAlert] = useState(false);
|
||||
const toast = useToast();
|
||||
|
||||
|
||||
|
||||
// ======================[ Cotext Api ]
|
||||
const { IODetails } = useContext(GlobalStateContext);
|
||||
const found = data?.find((item) => item?.id === actionId);
|
||||
|
||||
|
||||
const [addNavDetails] = useAddNavDetailsMutation()
|
||||
// const {
|
||||
// data
|
||||
// } = useGetArtifactsQuery(id)
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
watch,
|
||||
reset,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(ioNav),
|
||||
});
|
||||
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
|
||||
setIsLoading(true)
|
||||
|
||||
try {
|
||||
|
||||
const res = await addNavDetails({ data, id })
|
||||
if (res?.data?.statusCode === 201) {
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.data?.message} />,
|
||||
});
|
||||
handleClose()
|
||||
}else if(res?.error?.status === 400){
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
|
||||
});
|
||||
handleClose()
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleConfirm = () => {
|
||||
handleSubmit(onSubmit)();
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
handleSubmit(onSubmit)();
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setIsLoading(false);
|
||||
setAlert(false)
|
||||
onClose()
|
||||
reset({
|
||||
transactionDate:"",
|
||||
transactionAmount:"",
|
||||
comments:""
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const today = formatDatee(new Date(), 'yyyy-MM-dd');
|
||||
|
||||
function calculatePercentage(newNav, currNav) {
|
||||
const per = (newNav - currNav) / currNav * 100
|
||||
return per.toFixed(2)
|
||||
}
|
||||
|
||||
|
||||
console.log(calculatePercentage(1092500, 976070));
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Drawer
|
||||
size={"md"}
|
||||
isOpen={isOpen}
|
||||
placement="right"
|
||||
initialFocusRef={firstField}
|
||||
onClose={handleClose}
|
||||
>
|
||||
<DrawerOverlay />
|
||||
<DrawerContent>
|
||||
<DrawerCloseButton />
|
||||
<DrawerHeader fontSize={"sm"}>IO Nav Details</DrawerHeader>
|
||||
|
||||
<DrawerBody>
|
||||
<Stack spacing={4}>
|
||||
<FormControl isInvalid={errors.transactionDate} isRequired>
|
||||
<FormLabel fontSize={"sm"}>Date Selection</FormLabel>
|
||||
<Controller
|
||||
name="transactionDate"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Input {...field}
|
||||
max={today} // Set max attribute to today’s date
|
||||
fontSize={"sm"} type="date" size={"sm"} />
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.transactionDate?.message}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
|
||||
|
||||
|
||||
<FormControl isInvalid={errors.transactionAmount} isRequired>
|
||||
<FormLabel fontSize={"sm"}>New NAV</FormLabel>
|
||||
<Controller
|
||||
name="transactionAmount"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<CurrencyInput {...field} textAlign={'right'} fontSize={"sm"} type="number" size={"sm"} />
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.transactionAmount?.message}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
|
||||
|
||||
<HStack justify={'start'} gap={10} bg={'green.100'} p={3} rounded={'md'} shadow={'md'}>
|
||||
<VStack align={'start'}>
|
||||
<Text as={'span'} fontSize={'sm'} fontWeight={500}>Current nav</Text>
|
||||
<Text as={'span'} fontSize={'sm'}>
|
||||
{parseFloat(IODetails?.ioNAV || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Text>
|
||||
</VStack>
|
||||
|
||||
<VStack align={'start'}>
|
||||
<Text as={'span'} fontSize={'sm'} fontWeight={500}>Live return %</Text>
|
||||
<Text as={'span'} fontSize={'sm'}>{calculatePercentage(watch()?.transactionAmount||IODetails?.ioNAV,IODetails?.ioNAV)}</Text>
|
||||
</VStack>
|
||||
</HStack>
|
||||
|
||||
|
||||
|
||||
<FormControl isInvalid={errors.comments}>
|
||||
<FormLabel fontSize={"sm"}>Comment</FormLabel>
|
||||
<Controller
|
||||
name="comments"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Textarea {...field} maxLength={200} fontSize={"sm"} type="text" size={"sm"} />
|
||||
)}
|
||||
/>
|
||||
<FormErrorMessage fontSize={"xs"} fontWeight={500}>
|
||||
{errors.comments?.message}
|
||||
</FormErrorMessage>
|
||||
<FormHelperText fontSize="xs" color="gray.500">
|
||||
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
|
||||
{watch("comments")?.length || 0} characters.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
</Stack>
|
||||
</DrawerBody>
|
||||
|
||||
<DrawerFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
colorScheme={"forestGreen"}
|
||||
rounded={"sm"}
|
||||
size={"sm"}
|
||||
mr={3}
|
||||
onClick={handleClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
colorScheme={"forestGreen"}
|
||||
rounded={"sm"}
|
||||
size={"sm"}
|
||||
|
||||
onClick={() => setAlert(true)}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
|
||||
<CustomAlertDialog
|
||||
isOpen={alert}
|
||||
onClose={() => setAlert(false)}
|
||||
alertHandler={handleSave}
|
||||
message={"Are you sure you want to add NAV details?"}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddNavDetails;
|
||||
295
src/Pages/IO_Management/CreateIO/IONAVDetails/Approved.jsx
Normal file
@@ -0,0 +1,295 @@
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Text,
|
||||
Th,
|
||||
Tooltip,
|
||||
Tr,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||
import { AddIcon, DeleteIcon, EditIcon, ViewIcon } from "@chakra-ui/icons";
|
||||
import { LuFileSpreadsheet } from "react-icons/lu";
|
||||
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
|
||||
import NormalTable from "../../../../Components/DataTable/NormalTable";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
|
||||
import * as XLSX from "xlsx";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import AddCashDetails from "../AddCashDetails";
|
||||
import { debounce } from "../../../Admin/Contact";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
|
||||
import AddApproved from "./AddNavDetails";
|
||||
import AddNavDetails from "./AddNavDetails";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString();
|
||||
|
||||
const Approved = () => {
|
||||
const params = useParams();
|
||||
const toast = useToast();
|
||||
const id = params?.id;
|
||||
const firstField = useRef();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { IODetails, iONAVDetail, setIONAVDetail } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
const [updateIOCase] = useUpdateIOCaseMutation();
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
// Table filter
|
||||
const filteredData = IODetails?.ioNAVStatusHistory?.Approved?.filter(
|
||||
(item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item.transactionAmount;
|
||||
const searchLower = searchTerm?.toLowerCase();
|
||||
const nameMatches = name?.toLowerCase().includes(searchLower);
|
||||
return nameMatches;
|
||||
}
|
||||
);
|
||||
|
||||
const tableHeadRow = [
|
||||
"Sr No.",
|
||||
"Valuation date",
|
||||
"NAV",
|
||||
"Last Nav Update",
|
||||
"Investment Closed",
|
||||
"Comments",
|
||||
"Updated By",
|
||||
];
|
||||
|
||||
const extractedArray = filteredData?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr No.": (
|
||||
<Text as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
{index + 1}.
|
||||
</Text>
|
||||
),
|
||||
"Valuation date": (
|
||||
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
|
||||
{formatDate(item?.transactionDate)}
|
||||
</Text>
|
||||
),
|
||||
NAV: (
|
||||
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Text>
|
||||
),
|
||||
"Last Nav Update": (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.previousNAVvalue && `${item.previousNAVvalue}`}
|
||||
</Text>
|
||||
),
|
||||
"Investment Closed": (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item?.initialNAVvalue && `${item?.initialNAVvalue}`}
|
||||
</Text>
|
||||
),
|
||||
Comments: (
|
||||
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
{item?.comments ? item?.comments : "---"}
|
||||
</Text>
|
||||
),
|
||||
"Updated By": (
|
||||
<Text
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
{/* <Avatar
|
||||
mr={2}
|
||||
size="sm"
|
||||
name={item.creator?.firstName}
|
||||
src={item.creator?.profilePhoto}
|
||||
/> */}
|
||||
{item?.modifier?.firstName}
|
||||
</Text>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleAdd = async () => {
|
||||
try {
|
||||
const res = await updateIOCase(id);
|
||||
if (res?.data) {
|
||||
// toast({
|
||||
// render: () => (
|
||||
// <ToastBox status={"success"} message={res?.data?.message} />
|
||||
// ),
|
||||
// });
|
||||
setIsLoading(false);
|
||||
onOpen();
|
||||
} else if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedSponsors = sponser.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setCaseDetails(updatedSponsors);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
const exportToExcelNew = (data, fileName) => {
|
||||
const worksheet = XLSX.utils.json_to_sheet(data);
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
|
||||
|
||||
// Export file
|
||||
XLSX.writeFile(workbook, `${fileName}.xlsx`);
|
||||
};
|
||||
|
||||
const ioNavExport = IODetails?.ioNAVStatusHistory?.Approved?.map(
|
||||
(item, index) => ({
|
||||
ID: item?.id, // Keep as integer if it's already a number
|
||||
"Valuation date": formatDate(item?.transactionDate), // Assuming this is a date, no conversion needed
|
||||
NAV: parseFloat(item?.transactionAmount) || 0, // Convert to float
|
||||
"Last Nav Update": parseFloat(item?.previousNAVvalue) || 0, // Convert to float
|
||||
"Investment Closed": parseFloat(item?.initialNAVvalue) || 0, // Convert to float
|
||||
Comments: item?.comments, // Keep as string
|
||||
// "Transaction Type": item?.transactionType,
|
||||
"Updated By": item?.creator?.firstName, // Keep as string
|
||||
// "Update On": formatDate(item?.updatedAt) // Assuming this is a date, no conversion needed
|
||||
})
|
||||
);
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} pb={0}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
pb={3}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<HStack display={"flex"} alignItems={"center"}>
|
||||
<Button
|
||||
onClick={() => exportToExcelNew(ioNavExport, "Io Nav details")}
|
||||
leftIcon={<LuFileSpreadsheet />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
variant={"outline"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
isDisabled={ioNavExport?.length === 0}
|
||||
>
|
||||
Export xls
|
||||
</Button>
|
||||
{/* {IODetails?.isInvestedAmount
|
||||
? localStorage?.getItem("role") === "Maker" && (
|
||||
<Button
|
||||
onClick={handleAdd}
|
||||
leftIcon={<AddIcon />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
)
|
||||
: null} */}
|
||||
</HStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
emptyMessage={`We don't have any Sponers`}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
|
||||
<AddNavDetails
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
firstField={firstField}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Approved;
|
||||
192
src/Pages/IO_Management/CreateIO/IONAVDetails/IONAVDetails.jsx
Normal file
@@ -0,0 +1,192 @@
|
||||
// import { Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react";
|
||||
// import React from "react";
|
||||
// import Approved from "./Approved";
|
||||
// import Pending from "./Pending";
|
||||
// import Rejected from "./Rejected";
|
||||
|
||||
// const IONAVDetails = () => {
|
||||
// return (
|
||||
// <Tabs>
|
||||
// <TabList>
|
||||
// <Tab
|
||||
// fontSize={"sm"}
|
||||
// _selected={{
|
||||
// color: "#004118",
|
||||
// borderBottom: "2px solid #38a169",
|
||||
// }}
|
||||
// >
|
||||
// Approved
|
||||
// </Tab>
|
||||
// <Tab
|
||||
// fontSize={"sm"}
|
||||
// _selected={{
|
||||
// color: "#004118",
|
||||
// borderBottom: "2px solid #38a169",
|
||||
// }}
|
||||
// >
|
||||
// Pending
|
||||
// </Tab>
|
||||
// <Tab
|
||||
// fontSize={"sm"}
|
||||
// _selected={{
|
||||
// color: "#004118",
|
||||
// borderBottom: "2px solid #38a169",
|
||||
// }}
|
||||
// >
|
||||
// Rejected
|
||||
// </Tab>
|
||||
// </TabList>
|
||||
// <TabPanels>
|
||||
// <TabPanel>
|
||||
// <Approved />
|
||||
// </TabPanel>
|
||||
// <TabPanel>
|
||||
// <Pending />
|
||||
// </TabPanel>
|
||||
// <TabPanel>
|
||||
// <Rejected />
|
||||
// </TabPanel>
|
||||
// </TabPanels>
|
||||
// </Tabs>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default IONAVDetails;
|
||||
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
Tab,
|
||||
TabList,
|
||||
TabPanel,
|
||||
TabPanels,
|
||||
Tabs,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useRef } from "react";
|
||||
import Approved from "./Approved";
|
||||
import Pending from "./Pending";
|
||||
import Rejected from "./Rejected";
|
||||
import { AddIcon } from "@chakra-ui/icons";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import { useParams } from "react-router-dom";
|
||||
import AddNavDetails from "./AddNavDetails";
|
||||
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
|
||||
import { encryptString, isMaker } from "../../../../Constants/Constants";
|
||||
|
||||
const IONAVDetails = () => {
|
||||
const params = useParams();
|
||||
const toast = useToast();
|
||||
const id = params?.id;
|
||||
const { IODetails } = useContext(GlobalStateContext);
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const firstField = useRef();
|
||||
|
||||
const [updateIOCase] = useUpdateIOCaseMutation();
|
||||
|
||||
const handleAdd = async () => {
|
||||
try {
|
||||
const res = await updateIOCase(id);
|
||||
if (res?.data) {
|
||||
// toast({
|
||||
// render: () => (
|
||||
// <ToastBox status={"success"} message={res?.data?.message} />
|
||||
// ),
|
||||
// });
|
||||
// setIsLoading(false);
|
||||
onOpen();
|
||||
} else if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Tabs variant="unstyled">
|
||||
<Box
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
alignItems={"center"}
|
||||
borderBottom={"1px solid #ccc"}
|
||||
>
|
||||
<TabList>
|
||||
<Tab
|
||||
fontSize={"sm"}
|
||||
_selected={{
|
||||
color: "#004118",
|
||||
borderBottom: "2px solid #38a169",
|
||||
}}
|
||||
>
|
||||
Approved
|
||||
</Tab>
|
||||
<Tab
|
||||
fontSize={"sm"}
|
||||
_selected={{
|
||||
color: "#004118",
|
||||
borderBottom: "2px solid #38a169",
|
||||
}}
|
||||
>
|
||||
Pending
|
||||
{IODetails?.ioNAVStatusHistory?.Pending.length > 0 && (
|
||||
<Badge rounded={"sm"} colorScheme="forestGreen" ms={2}>
|
||||
{IODetails?.ioNAVStatusHistory?.Pending.length || 0}
|
||||
</Badge>
|
||||
)}
|
||||
</Tab>
|
||||
<Tab
|
||||
fontSize={"sm"}
|
||||
_selected={{
|
||||
color: "#004118",
|
||||
borderBottom: "2px solid #38a169",
|
||||
}}
|
||||
>
|
||||
Rejected
|
||||
</Tab>
|
||||
</TabList>
|
||||
{IODetails?.isInvestedAmount
|
||||
? isMaker() &&
|
||||
IODetails?.ioSatatus !== "Exited" && (
|
||||
<Button
|
||||
onClick={handleAdd}
|
||||
leftIcon={<AddIcon />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
)
|
||||
: null}
|
||||
</Box>
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<Approved />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<Pending />
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<Rejected />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
<AddNavDetails
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
firstField={firstField}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default IONAVDetails;
|
||||
353
src/Pages/IO_Management/CreateIO/IONAVDetails/Pending.jsx
Normal file
@@ -0,0 +1,353 @@
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Text,
|
||||
Tooltip,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
|
||||
import NormalTable from "../../../../Components/DataTable/NormalTable";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
|
||||
import { AddIcon, CheckIcon, CloseIcon, ViewIcon } from "@chakra-ui/icons";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import AddNavDetails from "./AddNavDetails";
|
||||
import RequestApproveModal from "./RequestApproveModal";
|
||||
import RequestRejectModal from "./RequestRejectModal";
|
||||
import { encryptString, isMaker } from "../../../../Constants/Constants";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString();
|
||||
|
||||
const Pending = () => {
|
||||
const params = useParams();
|
||||
const toast = useToast();
|
||||
const id = params?.id;
|
||||
const firstField = useRef();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { IODetails, iONAVDetail, setIONAVDetail } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
|
||||
const [updateIOCase] = useUpdateIOCaseMutation();
|
||||
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
onOpen: onConfirmOpen,
|
||||
onClose: onConfirmClose,
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isRejectOpen,
|
||||
onOpen: onRejectOpen,
|
||||
onClose: onRejectClose,
|
||||
} = useDisclosure();
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
// Table filter
|
||||
const filteredData = IODetails?.ioNAVStatusHistory?.Pending?.filter(
|
||||
(item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item?.transactionDate;
|
||||
const searchLower = searchTerm?.toLowerCase();
|
||||
const nameMatches = name?.toLowerCase().includes(searchLower);
|
||||
return nameMatches;
|
||||
}
|
||||
);
|
||||
|
||||
const tableHeadRow = [
|
||||
"Sr No.",
|
||||
"Valuation date",
|
||||
"NAV",
|
||||
"Last Nav Update",
|
||||
"Investment Closed",
|
||||
"Comments",
|
||||
"Updated By",
|
||||
...(!isMaker() ? ["Status"] : []),
|
||||
];
|
||||
|
||||
const extractedArray = filteredData?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr No.": (
|
||||
<Text as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
{index + 1}.
|
||||
</Text>
|
||||
),
|
||||
"Valuation date": (
|
||||
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
|
||||
{formatDate(item?.transactionDate)}
|
||||
</Text>
|
||||
),
|
||||
NAV: (
|
||||
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Text>
|
||||
),
|
||||
"Last Nav Update": (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.previousNAVvalue && `${item.previousNAVvalue}`}
|
||||
</Text>
|
||||
),
|
||||
"Investment Closed": (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item?.initialNAVvalue && `${item?.initialNAVvalue}`}
|
||||
</Text>
|
||||
),
|
||||
Comments: (
|
||||
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
{item?.comments ? item?.comments : "---"}
|
||||
</Text>
|
||||
),
|
||||
"Updated By": (
|
||||
<Text
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
{/* <Avatar
|
||||
mr={2}
|
||||
size="sm"
|
||||
name={item.creator?.firstName}
|
||||
src={item.creator?.profilePhoto}
|
||||
/> */}
|
||||
{item?.modifier?.firstName}
|
||||
</Text>
|
||||
),
|
||||
Status: isMaker() ? (
|
||||
<Button
|
||||
colorScheme="green"
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
px={2}
|
||||
py={1}
|
||||
fontWeight={500}
|
||||
onClick={() => {
|
||||
setActionId(item.id);
|
||||
}}
|
||||
>
|
||||
<ViewIcon me={"4px"} /> View
|
||||
</Button>
|
||||
) : (
|
||||
<Box display={"flex"} justifyContent={"center"}>
|
||||
<Box>
|
||||
<Box display={"flex"} justifyContent={"center"} gap={2}>
|
||||
<Tooltip
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
label="Approve"
|
||||
bg="#fff"
|
||||
color={"green.500"}
|
||||
placement="left-start"
|
||||
>
|
||||
<Button
|
||||
// colorScheme="forestGreen"
|
||||
// color="green.500"
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={2}
|
||||
py={1}
|
||||
onClick={() => {
|
||||
setActionId(item.id);
|
||||
onConfirmOpen();
|
||||
}}
|
||||
colorScheme="green"
|
||||
variant={"solid"}
|
||||
cursor={"pointer"}
|
||||
>
|
||||
<CheckIcon fontSize={"12px"} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
label="Reject"
|
||||
bg="#fff"
|
||||
color={"red.500"}
|
||||
placement="left-start"
|
||||
>
|
||||
<Button
|
||||
colorScheme="red"
|
||||
// color="red.500"
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={2}
|
||||
onClick={() => {
|
||||
setActionId(item.id);
|
||||
onRejectOpen();
|
||||
}}
|
||||
py={1}
|
||||
// variant={"solid"}
|
||||
>
|
||||
<CloseIcon fontSize={"10px"} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedSponsors = sponser.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setCaseDetails(updatedSponsors);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
const handleAdd = async () => {
|
||||
try {
|
||||
const res = await updateIOCase(id);
|
||||
if (res?.data) {
|
||||
// toast({
|
||||
// render: () => (
|
||||
// <ToastBox status={"success"} message={res?.data?.message} />
|
||||
// ),
|
||||
// });
|
||||
setIsLoading(false);
|
||||
onOpen();
|
||||
} else if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} pb={0}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
pb={3}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
{/* {IODetails?.isInvestedAmount
|
||||
? localStorage?.getItem("role") === "Maker" && (
|
||||
<Button
|
||||
onClick={handleAdd}
|
||||
leftIcon={<AddIcon />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
)
|
||||
: null} */}
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
emptyMessage={`We don't have any Sponers`}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<AddNavDetails
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
firstField={firstField}
|
||||
/>
|
||||
<RequestApproveModal
|
||||
// data={data?.data?.rows}
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
id={actionId}
|
||||
// firstField={firstField}
|
||||
/>
|
||||
<RequestRejectModal
|
||||
isOpen={isRejectOpen}
|
||||
onClose={onRejectClose}
|
||||
id={actionId}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Pending;
|
||||
257
src/Pages/IO_Management/CreateIO/IONAVDetails/Rejected.jsx
Normal file
@@ -0,0 +1,257 @@
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Table,
|
||||
Tag,
|
||||
Tbody,
|
||||
Text,
|
||||
Th,
|
||||
Tooltip,
|
||||
Tr,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||
import { AddIcon, DeleteIcon, EditIcon, ViewIcon } from "@chakra-ui/icons";
|
||||
import { LuFileSpreadsheet } from "react-icons/lu";
|
||||
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
|
||||
import NormalTable from "../../../../Components/DataTable/NormalTable";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import AddCashDetails from "../AddCashDetails";
|
||||
import { debounce } from "../../../Admin/Contact";
|
||||
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
|
||||
import { useParams } from "react-router-dom";
|
||||
import AddNavDetails from "./AddNavDetails";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString();
|
||||
|
||||
const Rejected = () => {
|
||||
const params = useParams();
|
||||
const toast = useToast();
|
||||
const id = params?.id;
|
||||
const firstField = useRef();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { IODetails, iONAVDetail, setIONAVDetail } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
const [updateIOCase] = useUpdateIOCaseMutation();
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
// Table filter
|
||||
const filteredData = IODetails?.ioNAVStatusHistory?.Reject?.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item.transactionAmount;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
return nameMatches;
|
||||
});
|
||||
|
||||
const tableHeadRow = [
|
||||
"Sr No.",
|
||||
"Valuation date",
|
||||
"NAV",
|
||||
"Last Nav Update",
|
||||
"Investment Closed",
|
||||
"Comments",
|
||||
"Updated By",
|
||||
];
|
||||
|
||||
const extractedArray = filteredData?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr No.": (
|
||||
<Text as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
{index + 1}.
|
||||
</Text>
|
||||
),
|
||||
"Valuation date": (
|
||||
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
|
||||
{formatDate(item?.transactionDate)}
|
||||
</Text>
|
||||
),
|
||||
NAV: (
|
||||
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Text>
|
||||
),
|
||||
"Last Nav Update": (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.previousNAVvalue && `${item.previousNAVvalue}`}
|
||||
</Text>
|
||||
),
|
||||
"Investment Closed": (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item?.initialNAVvalue && `${item?.initialNAVvalue}`}
|
||||
</Text>
|
||||
),
|
||||
Comments: (
|
||||
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
{item?.comments ? item?.comments : "---"}
|
||||
</Text>
|
||||
),
|
||||
"Updated By": (
|
||||
<Text
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
{/* <Avatar
|
||||
mr={2}
|
||||
size="sm"
|
||||
name={item.creator?.firstName}
|
||||
src={item.creator?.profilePhoto}
|
||||
/> */}
|
||||
{item?.modifier?.firstName}
|
||||
</Text>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedSponsors = sponser.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setCaseDetails(updatedSponsors);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
const handleAdd = async () => {
|
||||
try {
|
||||
const res = await updateIOCase(id);
|
||||
if (res?.data) {
|
||||
// toast({
|
||||
// render: () => (
|
||||
// <ToastBox status={"success"} message={res?.data?.message} />
|
||||
// ),
|
||||
// });
|
||||
setIsLoading(false);
|
||||
onOpen();
|
||||
} else if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} pb={0}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
pb={3}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
{/* {IODetails?.isInvestedAmount
|
||||
? localStorage?.getItem("role") === "Maker" && (
|
||||
<Button
|
||||
onClick={handleAdd}
|
||||
leftIcon={<AddIcon />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
)
|
||||
: null} */}
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
emptyMessage={`We don't have any Sponers`}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
|
||||
<AddNavDetails
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
firstField={firstField}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Rejected;
|
||||
@@ -0,0 +1,176 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import { useApproveIOCaseMutation, useApproveIONavMutation } from "../../../../Services/io.service";
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
// comments: yup.string().required("Comment is required")
|
||||
// .max(50, "Investment name cannot be more than 50 characters"),
|
||||
comments: yup
|
||||
.string()
|
||||
.required("Comment is required")
|
||||
.max(200, "Approve Comment cannot be more than 200 characters"),
|
||||
});
|
||||
|
||||
const RequestApproveModal = ({ isOpen, onClose, firstField ,id}) => {
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
const [ approveIONav ] = useApproveIONavMutation()
|
||||
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
console.log(data, "tewxttttt");
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await approveIONav({data,id})
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}else if(res?.data){
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.data?.message} />
|
||||
),
|
||||
});
|
||||
onClose()
|
||||
setIsBtnLoading(false)
|
||||
}else{
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={'error'} message={"Something went wrong"} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const selectedFile = event.target.files[0];
|
||||
setFile(selectedFile);
|
||||
};
|
||||
|
||||
|
||||
const { data, isLoading } =
|
||||
(id, {
|
||||
skip: !id,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
reset({
|
||||
investorAmount: data?.data?.investorAmount,
|
||||
});
|
||||
}
|
||||
}, [data, reset]);
|
||||
|
||||
const heandleOnClose = () =>{
|
||||
reset()
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
|
||||
<ModalOverlay />
|
||||
<ModalContent pb={4}>
|
||||
<ModalHeader fontSize={"md"}>Approve Comment</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
{isLoading ? (
|
||||
<FullscreenLoaders height={"50vh"} />
|
||||
) : (
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">Comment</FormLabel>
|
||||
<Textarea
|
||||
rows={6}
|
||||
focusBorderColor="green.400"
|
||||
name="comments"
|
||||
{...register("comments")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="md"
|
||||
placeholder={"Enter your checker comment...."}
|
||||
rounded={"md"}
|
||||
resize={"none"}
|
||||
maxLength={200}
|
||||
/>
|
||||
{errors.comments && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.comments.message}
|
||||
</Text>
|
||||
)}
|
||||
<FormHelperText fontSize="xs" color="gray.500">
|
||||
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered</Box>
|
||||
{watch("comments")?.length || 0} characters.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
colorScheme="gray"
|
||||
mr={3}
|
||||
onClick={onClose}
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RequestApproveModal;
|
||||