Compare commits
298 Commits
sprint4
...
Sprint-9.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c2a38becb | |||
|
|
212f5d4d37 | ||
|
|
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 | |||
| dfbc1ad338 | |||
| aa1c0c994a | |||
| 620b365437 | |||
| 2e5ecb967f |
28
.env.example
Normal file
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
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
2357
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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
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 />
|
||||
|
||||
@@ -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,171 @@ import {
|
||||
Tr,
|
||||
Skeleton,
|
||||
TableCaption,
|
||||
Tfoot,
|
||||
Checkbox,
|
||||
Radio,
|
||||
} from "@chakra-ui/react";
|
||||
import EmptySearchList from "../EmptySearchList";
|
||||
import { TABLE_PAGINATION } from "../../Constants/Paginations";
|
||||
|
||||
const DataTable = ({
|
||||
const NormalTable = ({
|
||||
data,
|
||||
isLoading,
|
||||
tableHeadRow,
|
||||
emptyMessage,
|
||||
centered,
|
||||
}) => {
|
||||
total,
|
||||
showRadioButton, // Prop for showing the checkboxes
|
||||
selectedRadio,
|
||||
setSelectedRadio, // State for managing selected checkboxes
|
||||
|
||||
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 +183,7 @@ const DataTable = ({
|
||||
}
|
||||
color={"gray.600"}
|
||||
key={i}
|
||||
fontWeight={500}
|
||||
style={{
|
||||
whiteSpace: "nowrap",
|
||||
textOverflow: "ellipsis",
|
||||
@@ -109,4 +202,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 Sharia 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]?.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;
|
||||
|
||||
65
src/Components/RoleSwitchButton.jsx
Normal file
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,2 @@
|
||||
export const TABLE_PAGINATION = { page: 1, size:20 }
|
||||
export const TABLE_PAGINATION = { page: 1, size: 20 }
|
||||
export const IMAGE_URI = import.meta.env.VITE_API_IMAGE_URL
|
||||
@@ -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,255 @@ 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 [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 +1751,22 @@ const GlobalStateProvider = ({ children }) => {
|
||||
setAcademicDocuments,
|
||||
iOArtifactsTwo,
|
||||
setIOArtifactsTwo,
|
||||
|
||||
|
||||
isIOloading,
|
||||
setIOloading
|
||||
InvestorWallet,
|
||||
setInvestorWallet,
|
||||
isIOloading,
|
||||
setIOloading,
|
||||
users,
|
||||
setUsers,
|
||||
fawateerRequest,
|
||||
setFawateerRequest,
|
||||
approveHistory,
|
||||
setApproveHistory,
|
||||
approved,
|
||||
setApproved,
|
||||
iONAVDetail,
|
||||
setIONAVDetail,
|
||||
iOTransaction,
|
||||
setIOTransaction,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -1,19 +1,48 @@
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import logo from "../assets/logo2.png";
|
||||
import logoDark from "../assets/logo.png";
|
||||
import logoMini from "../assets/logo-min.png";
|
||||
import logoMiniDark from "../assets/favicon.png";
|
||||
import { CgProfile } from "react-icons/cg";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { loginUser } from "../Redux/Slice/auth";
|
||||
import Button02 from "../Components/Buttons/Button02";
|
||||
import logoMiniDark from "../assets/favicon.png";
|
||||
import logoMini from "../assets/logo-min.png";
|
||||
import logoDark from "../assets/logo.png";
|
||||
import logo from "../assets/logo2.png";
|
||||
|
||||
import {
|
||||
ArrowBackIcon,
|
||||
ArrowLeftIcon,
|
||||
ArrowRightIcon,
|
||||
AtSignIcon,
|
||||
} from "@chakra-ui/icons";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionButton,
|
||||
AccordionIcon,
|
||||
AccordionItem,
|
||||
AccordionPanel,
|
||||
Alert,
|
||||
AlertIcon,
|
||||
Box,
|
||||
Button,
|
||||
Image,
|
||||
Text,
|
||||
Tooltip
|
||||
} from "@chakra-ui/react";
|
||||
import Cookies from "js-cookie"; // Import the Cookies library
|
||||
import { GrManual } from "react-icons/gr";
|
||||
import { HiOutlineChartSquareBar } from "react-icons/hi";
|
||||
import { LuContact } from "react-icons/lu";
|
||||
import { MdNotificationsNone, MdOutlineAddChart } from "react-icons/md";
|
||||
import {
|
||||
RiBankLine,
|
||||
RiExchangeBoxLine,
|
||||
RiFileUserLine,
|
||||
RiMoneyDollarBoxLine,
|
||||
} from "react-icons/ri";
|
||||
import {
|
||||
TbArrowBadgeLeftFilled,
|
||||
TbListDetails,
|
||||
TbReportMoney,
|
||||
TbTransactionDollar,
|
||||
TbTransactionDollar
|
||||
} from "react-icons/tb";
|
||||
import { TbArrowBadgeRightFilled } from "react-icons/tb";
|
||||
import { ArrowBackIcon, ArrowLeftIcon, ArrowRightIcon } from "@chakra-ui/icons";
|
||||
import { VscSymbolClass } from "react-icons/vsc";
|
||||
import {
|
||||
Link,
|
||||
NavLink,
|
||||
@@ -22,61 +51,21 @@ import {
|
||||
useLocation,
|
||||
useNavigate,
|
||||
} from "react-router-dom";
|
||||
import { RouteLink } from "../Routes/Routes";
|
||||
import NotFound from "../Pages/NotFound";
|
||||
import { nav } from "../Routes/Nav";
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
PopoverArrow,
|
||||
PopoverBody,
|
||||
PopoverCloseButton,
|
||||
PopoverContent,
|
||||
PopoverFooter,
|
||||
PopoverHeader,
|
||||
PopoverTrigger,
|
||||
Portal,
|
||||
Text,
|
||||
WrapItem,
|
||||
Popover,
|
||||
Tag,
|
||||
Accordion,
|
||||
AccordionItem,
|
||||
AccordionButton,
|
||||
AccordionIcon,
|
||||
AccordionPanel,
|
||||
Image,
|
||||
Alert,
|
||||
AlertIcon,
|
||||
Breadcrumb,
|
||||
Divider,
|
||||
} from "@chakra-ui/react";
|
||||
import GlobalStateContext from "../Contexts/GlobalStateContext";
|
||||
import Cookies from "js-cookie"; // Import the Cookies library
|
||||
import Header from "../Components/Header";
|
||||
import HeaderMain from "../Components/HeaderMain";
|
||||
import { IoMdSwap } from "react-icons/io";
|
||||
import {
|
||||
RiBankLine,
|
||||
RiExchangeBoxLine,
|
||||
RiFileUserLine,
|
||||
RiMoneyDollarBoxLine,
|
||||
} from "react-icons/ri";
|
||||
import { VscSymbolClass } from "react-icons/vsc";
|
||||
import { MdNotificationsNone, MdOutlineAddChart } from "react-icons/md";
|
||||
import { HiOutlineChartSquareBar } from "react-icons/hi";
|
||||
import { GrManual } from "react-icons/gr";
|
||||
import { LuContact } from "react-icons/lu";
|
||||
import shield from "../assets/shield.png";
|
||||
import GlobalStateContext from "../Contexts/GlobalStateContext";
|
||||
import CreateRequest from "../Pages/Fawateer/CreateRequest";
|
||||
import ApproveHistory from "../Pages/FawateerChecker/ApproveHistory/ApproveHistoryChecker";
|
||||
import ApproveHistoryMaker from "../Pages/FawateerChecker/ApproveHistory/ApproveHistoryMaker";
|
||||
import ApproveRequest from "../Pages/FawateerChecker/ApproveRequest/ApproveRequest";
|
||||
import NotFound from "../Pages/NotFound";
|
||||
import SplashScreen from "../Pages/SplashScreen";
|
||||
import CutomBreadcrumb from "../Components/CutomBreadcrumb";
|
||||
import CustomBreadcrumb from "../Components/CutomBreadcrumb";
|
||||
import { getCountdownTimer } from "../Constants/Constants";
|
||||
import { nav } from "../Routes/Nav";
|
||||
import { RouteLink } from "../Routes/Routes";
|
||||
import { useProfileQuery } from "../Services/io.service";
|
||||
import { useLogoutMutation } from "../Services/token.serivce";
|
||||
|
||||
const DashboardLayout = ({ isOnline }) => {
|
||||
const navigate = useNavigate();
|
||||
const dispach = useDispatch();
|
||||
const location = useLocation();
|
||||
const path = location.pathname;
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
@@ -91,6 +80,18 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
const [isSplashVisible, setSplashVisible] = useState(true);
|
||||
const [openIndex, setOpenIndex] = useState(null);
|
||||
|
||||
const { data, refetch } = useProfileQuery();
|
||||
|
||||
// useEffect(() => {
|
||||
// if (
|
||||
// !localStorage.getItem("accessToken") &&
|
||||
// !localStorage.getItem("refreshToken")
|
||||
// ) {
|
||||
// logOutHandler();
|
||||
// return navigate("/login");
|
||||
// }
|
||||
// }, []);
|
||||
|
||||
useEffect(() => {
|
||||
const savedIndex = localStorage.getItem("openAccordionIndex");
|
||||
if (savedIndex !== null) {
|
||||
@@ -108,8 +109,8 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
// Set a timer to hide the splash screen after 3 seconds
|
||||
const timer = setTimeout(() => {
|
||||
setSplashVisible(false);
|
||||
},1000); // 3000ms = 3 seconds
|
||||
|
||||
}, 1000); // 3000ms = 3 seconds
|
||||
refetch();
|
||||
// Cleanup the timer
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
@@ -118,14 +119,19 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
setOpenDrawerClick(!openDrawerClick);
|
||||
};
|
||||
|
||||
const logOutHandler = () => {
|
||||
const [logout] = useLogoutMutation();
|
||||
|
||||
const logOutHandler = async () => {
|
||||
// dispach(loginUser(false));
|
||||
setIsAuthenticate(false);
|
||||
Cookies.remove("isAuthenticated");
|
||||
localStorage.removeItem('refreshToken')
|
||||
localStorage.removeItem('accessToken')
|
||||
localStorage.removeItem('refreshTokenExp')
|
||||
navigate("/login");
|
||||
try {
|
||||
await logout();
|
||||
localStorage.clear();
|
||||
navigate("/login");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
// // Function to get the title based on the route
|
||||
@@ -139,12 +145,24 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
<RiMoneyDollarBoxLine className="h4 m-0" /> Sponsor
|
||||
</span>
|
||||
);
|
||||
case path.startsWith("/email"):
|
||||
return (
|
||||
<span className="d-flex align-items-end gap-2">
|
||||
<AtSignIcon className="h4 m-0" /> Email Notifiation
|
||||
</span>
|
||||
);
|
||||
case path.startsWith("/investment-type"):
|
||||
return (
|
||||
<span className="d-flex align-items-end gap-2">
|
||||
<VscSymbolClass className="h4 m-0" /> Investment Type
|
||||
</span>
|
||||
);
|
||||
case path.startsWith("/profile"):
|
||||
return (
|
||||
<span className="d-flex align-items-end gap-2">
|
||||
<CgProfile className="h4 m-0" /> Profile
|
||||
</span>
|
||||
);
|
||||
case path.startsWith("/exchange-rate"):
|
||||
return (
|
||||
<span className="d-flex align-items-end gap-2">
|
||||
@@ -189,28 +207,35 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
</span>
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
case path.startsWith("/deposit-request"):
|
||||
return (
|
||||
<span className="d-flex align-items-end gap-2">
|
||||
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
|
||||
Deposite pending request
|
||||
Deposit Pending Request
|
||||
</span>
|
||||
);
|
||||
case path.startsWith("/deposit-history"):
|
||||
return (
|
||||
<span className="d-flex align-items-end gap-2">
|
||||
<RiExchangeBoxLine className="h4 m-0 fw-normal" />
|
||||
Deposite withdrawal request
|
||||
Deposite Request
|
||||
</span>
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
case path.startsWith("/fawateer"):
|
||||
return (
|
||||
<span className="d-flex align-items-end gap-2">
|
||||
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
|
||||
Fawateer Deposit
|
||||
</span>
|
||||
);
|
||||
case path.startsWith("/fawateer-history"):
|
||||
return (
|
||||
<span className="d-flex align-items-end gap-2">
|
||||
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
|
||||
Fawateer Deposit
|
||||
</span>
|
||||
);
|
||||
|
||||
case path.startsWith("/withdraw-request"):
|
||||
return (
|
||||
@@ -324,6 +349,13 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
Deletion request
|
||||
</span>
|
||||
);
|
||||
case path.startsWith("/subadmin"):
|
||||
return (
|
||||
<span className="d-flex align-items-end gap-2">
|
||||
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
|
||||
Manage SubAdmin
|
||||
</span>
|
||||
);
|
||||
|
||||
default:
|
||||
if (path.startsWith("/community/view/")) {
|
||||
@@ -353,6 +385,19 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
return <SplashScreen />;
|
||||
}
|
||||
|
||||
const filteredNav = nav.map((item) => {
|
||||
if (item.submenu) {
|
||||
return {
|
||||
...item,
|
||||
submenu: item.submenu.filter(
|
||||
(submenuItem) =>
|
||||
!(!data?.data?.superAdmin && submenuItem.title === "Sub Admin")
|
||||
),
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
return (
|
||||
<Box
|
||||
style={{
|
||||
@@ -386,7 +431,7 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
bottom={4}
|
||||
right={!slideFromRight ? 4 : "auto"}
|
||||
left={slideFromRight ? 4 : "auto"}
|
||||
backgroundColor={"#fff"}
|
||||
backgroundColor={"#000"}
|
||||
rounded={"full"}
|
||||
p={2}
|
||||
w={8}
|
||||
@@ -411,7 +456,7 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
|
||||
{slideFromRight ? null : (
|
||||
<aside
|
||||
className="h-100 position-relative sideBar "
|
||||
className="h-100 position-relative sideBar"
|
||||
// onMouseOver={() => setIsDrawerOpen(true)}
|
||||
// onMouseLeave={() => setIsDrawerOpen(false)}
|
||||
style={{
|
||||
@@ -438,7 +483,7 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
}}
|
||||
src={colorMode === "light" ? logo : logoDark}
|
||||
alt="Logo"
|
||||
onClick={()=> navigate('/')}
|
||||
onClick={() => navigate("/")}
|
||||
cursor={"pointer"}
|
||||
/>
|
||||
) : (
|
||||
@@ -448,7 +493,7 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
}}
|
||||
src={colorMode === "light" ? logoMini : logoMiniDark}
|
||||
alt="Logo"
|
||||
onClick={()=> navigate('/')}
|
||||
onClick={() => navigate("/")}
|
||||
cursor={"pointer"}
|
||||
/>
|
||||
)}
|
||||
@@ -470,44 +515,57 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
index={openIndex}
|
||||
onChange={handleAccordionChange}
|
||||
>
|
||||
{nav.map(({ title, type, Icon, submenu, path }, index) => {
|
||||
{filteredNav.map(({ title, type, Icon, submenu, path }, index) => {
|
||||
if (type === "accordion") {
|
||||
return (
|
||||
<AccordionItem key={index} border={"none"}>
|
||||
<AccordionButton
|
||||
style={{ height: "auto" }}
|
||||
className={`${
|
||||
isDrawerOpen || openDrawerClick
|
||||
? "p-2 web-text-medium ps-3 justify-content-between"
|
||||
: "p-2 ps-1 web-text-xlarge justify-content-center"
|
||||
} rounded-1 link d-flex align-items-center gap-2 w-100 mb-1`}
|
||||
<Tooltip
|
||||
isDisabled={isDrawerOpen || openDrawerClick}
|
||||
hasArrow
|
||||
bg={"#fff"}
|
||||
fontSize={"xs"}
|
||||
label={title}
|
||||
placement="top-start"
|
||||
color={"blue.800"}
|
||||
>
|
||||
<Box
|
||||
as="span"
|
||||
display={"flex"}
|
||||
gap={2}
|
||||
alignItems={"center"}
|
||||
<AccordionButton
|
||||
style={{ height: "auto" }}
|
||||
className={`${
|
||||
isDrawerOpen || openDrawerClick
|
||||
? "p-2 web-text-medium ps-3 justify-content-between"
|
||||
: "p-2 ps-1 web-text-xlarge justify-content-center"
|
||||
} rounded-1 link d-flex align-items-center gap-2 w-100 mb-1`}
|
||||
>
|
||||
{/* {Icon && title === "Admin" ? <Image w={15} src={shield} /> : <Icon className={`web-text-large`} />} */}
|
||||
{Icon && (
|
||||
<Icon
|
||||
fontSize={title === "Admin" ? "18px" : "15px"}
|
||||
/>
|
||||
)}
|
||||
<Text
|
||||
as={"span"}
|
||||
display={
|
||||
isDrawerOpen || openDrawerClick ? "flex" : "none"
|
||||
}
|
||||
alignItems="center"
|
||||
overflow="hidden"
|
||||
textAlign={"left"}
|
||||
<Box
|
||||
as="span"
|
||||
display={"flex"}
|
||||
gap={2}
|
||||
alignItems={"center"}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
</Box>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
{/* {Icon && title === "Admin" ? <Image w={15} src={shield} /> : <Icon className={`web-text-large`} />} */}
|
||||
{Icon && (
|
||||
<Icon
|
||||
fontSize={title === "Admin" ? "18px" : "15px"}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Text
|
||||
as={"span"}
|
||||
display={
|
||||
isDrawerOpen || openDrawerClick
|
||||
? "flex"
|
||||
: "none"
|
||||
}
|
||||
alignItems="center"
|
||||
overflow="hidden"
|
||||
textAlign={"left"}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
</Box>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
</Tooltip>
|
||||
<AccordionPanel
|
||||
p={0}
|
||||
pb={1}
|
||||
@@ -520,65 +578,81 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
{ title: subMenuTitle, path: link, icon: SubIcon },
|
||||
i
|
||||
) => (
|
||||
<Box
|
||||
key={i}
|
||||
style={{ height: "auto", position: "relative" }}
|
||||
className={`${
|
||||
isDrawerOpen || openDrawerClick
|
||||
? " web-text-medium ps-4"
|
||||
: " web-text-xlarge justify-content-center"
|
||||
} d-flex align-items-center p-0`}
|
||||
<Tooltip
|
||||
isDisabled={isDrawerOpen || openDrawerClick}
|
||||
hasArrow
|
||||
bg={"#fff"}
|
||||
fontSize={"xs"}
|
||||
label={subMenuTitle}
|
||||
placement="right"
|
||||
color={"blue.800"}
|
||||
>
|
||||
<Box
|
||||
backgroundColor={"gray.300"}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
width: 2,
|
||||
left: 22,
|
||||
height:
|
||||
i === submenu?.length - 1 ? "55%" : "120%",
|
||||
borderRadius: "0 0 10px 10px",
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
backgroundColor={"gray.300"}
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: 10,
|
||||
left: 22,
|
||||
height: 2,
|
||||
}}
|
||||
/>
|
||||
|
||||
<NavLink
|
||||
key={i}
|
||||
style={{ height: "auto", position: "relative" }}
|
||||
className={`${
|
||||
isDrawerOpen || openDrawerClick
|
||||
? "p-2 ps-1 ms-2 web-text-medium "
|
||||
: "p-2 ps-0 ms-0 zindex-3 ms-4 web-text-xlarge justify-content-center"
|
||||
} rounded-1 link d-flex align-items-center gap-2 w-100 `}
|
||||
to={link}
|
||||
? " web-text-medium ps-4"
|
||||
: " web-text-xlarge justify-content-center"
|
||||
} d-flex align-items-center p-0`}
|
||||
>
|
||||
{SubIcon && (
|
||||
<SubIcon
|
||||
className="web-text-large ms-2"
|
||||
style={{ zIndex: 111 }}
|
||||
/>
|
||||
)}
|
||||
<Text
|
||||
as={"span"}
|
||||
display={
|
||||
<Box
|
||||
backgroundColor={"gray.300"}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
width: 2,
|
||||
left: 22,
|
||||
height:
|
||||
i === submenu?.length - 1
|
||||
? "55%"
|
||||
: "120%",
|
||||
borderRadius: "0 0 10px 10px",
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
backgroundColor={"gray.300"}
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: 10,
|
||||
left: 22,
|
||||
height: 2,
|
||||
}}
|
||||
/>
|
||||
|
||||
<NavLink
|
||||
className={`${
|
||||
isDrawerOpen || openDrawerClick
|
||||
? "flex"
|
||||
: "none"
|
||||
}
|
||||
alignItems="center"
|
||||
overflow="hidden"
|
||||
? "p-2 ps-1 ms-2 web-text-medium "
|
||||
: "p-2 ps-0 ms-0 zindex-3 ms-4 web-text-xlarge justify-content-center"
|
||||
} rounded-1 link d-flex align-items-center gap-2 w-100 `}
|
||||
to={link}
|
||||
>
|
||||
{subMenuTitle}
|
||||
</Text>
|
||||
</NavLink>
|
||||
</Box>
|
||||
{SubIcon && (
|
||||
<SubIcon
|
||||
className="web-text-large ms-2"
|
||||
style={{ zIndex: 111 }}
|
||||
/>
|
||||
)}
|
||||
<Text
|
||||
as={"span"}
|
||||
display={
|
||||
isDrawerOpen || openDrawerClick
|
||||
? "flex"
|
||||
: "none"
|
||||
}
|
||||
alignItems="center"
|
||||
overflow="hidden"
|
||||
>
|
||||
{subMenuTitle === "Aprover Request"
|
||||
? data?.data?.role === "Maker"
|
||||
? "Create Request"
|
||||
: "Aprover Request"
|
||||
: subMenuTitle}
|
||||
</Text>
|
||||
</NavLink>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
)
|
||||
)}
|
||||
</AccordionPanel>
|
||||
@@ -596,28 +670,37 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
);
|
||||
} else if (type === "single") {
|
||||
return (
|
||||
<NavLink
|
||||
key={index}
|
||||
style={{ height: "auto", position: "relative" }}
|
||||
className={`${
|
||||
isDrawerOpen || openDrawerClick
|
||||
? "p-2 web-text-medium"
|
||||
: "p-2 ps-0 web-text-xlarge justify-content-start"
|
||||
} rounded-1 link d-flex align-items-center gap-2 w-100`}
|
||||
to={path}
|
||||
<Tooltip
|
||||
hasArrow
|
||||
bg={"#fff"}
|
||||
fontSize={"xs"}
|
||||
label={title}
|
||||
placement="top-start"
|
||||
color={"blue.800"}
|
||||
>
|
||||
{Icon && <Icon className="web-text-large ms-2" />}
|
||||
<Text
|
||||
as={"span"}
|
||||
display={
|
||||
isDrawerOpen || openDrawerClick ? "flex" : "none"
|
||||
}
|
||||
alignItems="center"
|
||||
overflow="hidden"
|
||||
<NavLink
|
||||
key={index}
|
||||
style={{ height: "auto", position: "relative" }}
|
||||
className={`${
|
||||
isDrawerOpen || openDrawerClick
|
||||
? "p-2 web-text-medium"
|
||||
: "p-2 ps-0 web-text-xlarge justify-content-start"
|
||||
} rounded-1 link d-flex align-items-center gap-2 w-100`}
|
||||
to={path}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
</NavLink>
|
||||
{Icon && <Icon className="web-text-large ms-2" />}
|
||||
<Text
|
||||
as={"span"}
|
||||
display={
|
||||
isDrawerOpen || openDrawerClick ? "flex" : "none"
|
||||
}
|
||||
alignItems="center"
|
||||
overflow="hidden"
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
</NavLink>
|
||||
</Tooltip>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
@@ -626,7 +709,7 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
</Accordion>
|
||||
</Box>
|
||||
|
||||
{/* <Button
|
||||
<Button
|
||||
colorScheme={"forestGreen"}
|
||||
rounded={"lg"}
|
||||
// onMouseOver={() => setIsDrawerOpen(true)}
|
||||
@@ -646,18 +729,18 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
) : (
|
||||
<ArrowRightIcon className="web-text-small " />
|
||||
)}
|
||||
</Button> */}
|
||||
<Text textAlign={'center'} fontWeight={500} fontSize={'xs'} color={"gray.600"}>{getCountdownTimer(localStorage.getItem('accessTokenExp'))}</Text>
|
||||
|
||||
</Button>
|
||||
{/* <Text textAlign={'center'} fontWeight={500} fontSize={'xs'} color={"gray.600"}>{getCountdownTimer(localStorage.getItem('accessTokenExp'))}</Text> */}
|
||||
</aside>
|
||||
)}
|
||||
|
||||
<main
|
||||
className={`h-100 ${slideFromRight ? "pe-3" : "ps-3"} d-flex flex-column gap-0`}
|
||||
className={`h-100 ${
|
||||
slideFromRight ? "pe-3" : "ps-3"
|
||||
} d-flex flex-column gap-0`}
|
||||
style={{
|
||||
width: `calc(100% - ${isDrawerOpen || openDrawerClick ? 230 : 74}px)`,
|
||||
transition: "width 0.3s ease-in-out",
|
||||
|
||||
}}
|
||||
>
|
||||
{/* <header className="p-2 ps-0 pt-3 fw-400 border-bottom">
|
||||
@@ -665,261 +748,47 @@ const DashboardLayout = ({ isOnline }) => {
|
||||
</header> */}
|
||||
|
||||
<HeaderMain
|
||||
data={data}
|
||||
slideDirecttion={slideFromRight}
|
||||
logOutHandler={logOutHandler}
|
||||
icon
|
||||
title={getTitle()}
|
||||
/>
|
||||
|
||||
{/* <CustomBreadcrumb /> */}
|
||||
|
||||
<AppContent />
|
||||
{/* <CustomBreadcrumb /> */}
|
||||
|
||||
<AppContent data={data} />
|
||||
</main>
|
||||
|
||||
{/* =======[ Left ]============ */}
|
||||
|
||||
{slideFromRight ? (
|
||||
<aside
|
||||
className="h-100 position-relative sideBar pe-1"
|
||||
// onMouseOver={() => setIsDrawerOpen(true)}
|
||||
// onMouseLeave={() => setIsDrawerOpen(false)}
|
||||
style={{
|
||||
width: isDrawerOpen || openDrawerClick ? 232 : 74,
|
||||
transition: "width 0.3s ease-in-out", // Smooth transition for width change
|
||||
// overflow: "hidden",
|
||||
backgroundColor: "#0041180A",
|
||||
position: "relative",
|
||||
// backgroundColor: "#002F0F",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={`d-flex ${
|
||||
isDrawerOpen || openDrawerClick
|
||||
? "justify-content-end"
|
||||
: "justify-content-center"
|
||||
} p-3 pt-3 pb-4 position-relative `}
|
||||
height={"10%"}
|
||||
>
|
||||
{isDrawerOpen || openDrawerClick ? (
|
||||
<img
|
||||
style={{
|
||||
width: 120,
|
||||
}}
|
||||
src={logo}
|
||||
alt="Logo"
|
||||
/>
|
||||
) : (
|
||||
<img
|
||||
style={{
|
||||
width: 30,
|
||||
}}
|
||||
src={logoMini}
|
||||
alt="Logo"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Box
|
||||
className="ps-2 scroll-bar"
|
||||
style={{ height: "80%", overflowY: "scroll", overflowX: "hidden" }}
|
||||
>
|
||||
<Accordion m={0} allowToggle>
|
||||
{nav.map(({ title, type, Icon, submenu, path }, index) => {
|
||||
if (type === "accordion") {
|
||||
return (
|
||||
<AccordionItem key={index} border={"none"}>
|
||||
<AccordionButton
|
||||
style={{ height: "auto" }}
|
||||
className={`${
|
||||
isDrawerOpen || openDrawerClick
|
||||
? "p-2 web-text-medium ps-3 justify-content-between"
|
||||
: "p-2 ps-1 web-text-xlarge justify-content-center"
|
||||
} rounded-1 link d-flex align-items-center gap-2 w-100 mb-1`}
|
||||
flexDirection={"row-reverse"}
|
||||
>
|
||||
<Box
|
||||
as="span"
|
||||
display={"flex"}
|
||||
gap={2}
|
||||
alignItems={"center"}
|
||||
flexDirection={"row-reverse"}
|
||||
>
|
||||
{Icon && <Icon className="web-text-large" />}
|
||||
<Text
|
||||
as={"span"}
|
||||
display={
|
||||
isDrawerOpen || openDrawerClick ? "flex" : "none"
|
||||
}
|
||||
alignItems="center"
|
||||
overflow="hidden"
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
</Box>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
<AccordionPanel
|
||||
p={0}
|
||||
pb={1}
|
||||
display={"flex"}
|
||||
flexDirection={"column"}
|
||||
gap={1}
|
||||
>
|
||||
{submenu?.map(
|
||||
(
|
||||
{ title: subMenuTitle, path: link, icon: SubIcon },
|
||||
i
|
||||
) => (
|
||||
<Box
|
||||
key={i}
|
||||
style={{ height: "auto", position: "relative" }}
|
||||
className={`${
|
||||
isDrawerOpen || openDrawerClick
|
||||
? " web-text-medium ps-0 pe-4"
|
||||
: " web-text-xlarge justify-content-center"
|
||||
} d-flex align-items-center p-0`}
|
||||
>
|
||||
<Box
|
||||
backgroundColor={"gray.300"}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
width: 2,
|
||||
right: 20,
|
||||
height:
|
||||
i === submenu?.length - 1 ? "55%" : "120%",
|
||||
borderRadius: "0 0 10px 10px",
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
backgroundColor={"gray.300"}
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: 8,
|
||||
right: 20,
|
||||
height: 2,
|
||||
}}
|
||||
/>
|
||||
|
||||
<NavLink
|
||||
flexDirection={"row-reverse"}
|
||||
className={`${
|
||||
isDrawerOpen || openDrawerClick
|
||||
? "p-2 ps-1 me-1 web-text-medium "
|
||||
: "p-2 ps-0 ms-0 zindex-3 ms-4 web-text-xlarge justify-content-center"
|
||||
} rounded-1 link d-flex align-items-center gap-2 w-100 flex-direction-row-reverse`}
|
||||
to={link}
|
||||
style={{ flexDirection: "row-reverse" }}
|
||||
>
|
||||
{SubIcon && (
|
||||
<SubIcon
|
||||
className="web-text-large ms-0"
|
||||
style={{ zIndex: 111 }}
|
||||
/>
|
||||
)}
|
||||
<Text
|
||||
as={"span"}
|
||||
display={
|
||||
isDrawerOpen || openDrawerClick
|
||||
? "flex"
|
||||
: "none"
|
||||
}
|
||||
alignItems="center"
|
||||
overflow="hidden"
|
||||
>
|
||||
{subMenuTitle}
|
||||
</Text>
|
||||
</NavLink>
|
||||
</Box>
|
||||
)
|
||||
)}
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
);
|
||||
} else if (type === "title") {
|
||||
return (
|
||||
<Text
|
||||
as={"span"}
|
||||
key={index}
|
||||
className="web-text-xxsmall fw-600 mt-1 text-secondary fw-bold me-2"
|
||||
padding={0}
|
||||
display={"flex"}
|
||||
justifyContent={"end"}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
);
|
||||
} else if (type === "single") {
|
||||
return (
|
||||
<NavLink
|
||||
key={index}
|
||||
style={{
|
||||
height: "auto",
|
||||
position: "relative",
|
||||
flexDirection: "row-reverse",
|
||||
}}
|
||||
className={`${
|
||||
isDrawerOpen || openDrawerClick
|
||||
? "p-2 web-text-medium"
|
||||
: "p-2 ps-0 web-text-xlarge justify-content-start"
|
||||
} rounded-1 link d-flex align-items-center gap-2 w-100`}
|
||||
to={path}
|
||||
>
|
||||
{Icon && <Icon className="web-text-large ms-2" />}
|
||||
<Text
|
||||
as={"span"}
|
||||
display={
|
||||
isDrawerOpen || openDrawerClick ? "flex" : "none"
|
||||
}
|
||||
alignItems="center"
|
||||
overflow="hidden"
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
</NavLink>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</Accordion>
|
||||
</Box>
|
||||
|
||||
<Button
|
||||
colorScheme={"forestGreen"}
|
||||
rounded={"lg"}
|
||||
// onMouseOver={() => setIsDrawerOpen(true)}
|
||||
// onMouseLeave={() => setIsDrawerOpen(false)}
|
||||
onClick={openDrawerOnClick}
|
||||
style={{
|
||||
width: 18,
|
||||
height: 26,
|
||||
position: "absolute",
|
||||
left: -18,
|
||||
bottom: 28,
|
||||
zIndex: 99,
|
||||
}}
|
||||
>
|
||||
{isDrawerOpen || openDrawerClick ? (
|
||||
<ArrowRightIcon className="web-text-small " />
|
||||
) : (
|
||||
<ArrowLeftIcon className="web-text-small" />
|
||||
)}
|
||||
</Button>
|
||||
</aside>
|
||||
) : null}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardLayout;
|
||||
|
||||
const AppContent = () => {
|
||||
const AppContent = ({ data }) => {
|
||||
return (
|
||||
<Routes>
|
||||
{RouteLink.map(({ path, Component }, index) => (
|
||||
<Route key={index} path={path} element={<Component />} />
|
||||
))}
|
||||
|
||||
<Route
|
||||
path="/fawateer"
|
||||
element={
|
||||
data?.data?.role === "Maker" ? <CreateRequest /> : <ApproveRequest />
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/fawateer-history"
|
||||
element={
|
||||
data?.data?.role === "Maker" ? (
|
||||
<ApproveHistoryMaker />
|
||||
) : (
|
||||
<ApproveHistory />
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
);
|
||||
|
||||
@@ -11,66 +11,84 @@ import {
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
||||
import DataTable from "../../Components/DataTable/DataTable";
|
||||
import NormalTable from "../../Components/DataTable/NormalTable";
|
||||
import Pagination from "../../Components/Pagination";
|
||||
import GlobalStateContext from "../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../Components/CustomAlertDialog";
|
||||
import { formatDate } from "../../Components/Functions/UTCConvertor";
|
||||
import { CheckIcon, CloseIcon } from "@chakra-ui/icons";
|
||||
import { useGetDeleteHistoryQuery } from "../../Services/delete.request.service";
|
||||
import { TABLE_PAGINATION } from "../../Constants/Paginations";
|
||||
// import { formatDate } from "../../Components/Functions/UTCConvertor";
|
||||
|
||||
const DeletionHistory = () => {
|
||||
const toast = useToast();
|
||||
const { slideFromRight, deleteHistory, setDeleteHistory } =
|
||||
const { slideFromRight, setDeleteHistory } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
// ====================================================[Table Filter]================================================================
|
||||
const filteredData = deleteHistory.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item.Distribution;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
// =========================== [Use State] =============================
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
|
||||
// Filter by status
|
||||
// const status = item.status;
|
||||
// const statusLower = status ? "active" : "inactive";
|
||||
// Debounce the search term to avoid making a request on every keystroke
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedSearchTerm(searchTerm);
|
||||
}, 500); // Adjust delay as needed
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [searchTerm]);
|
||||
|
||||
// const statusMatches =
|
||||
// statusFilter === "all" ||
|
||||
// (statusFilter === "active" && status === true) ||
|
||||
// (statusFilter === "inactive" && status === false);
|
||||
|
||||
return nameMatches;
|
||||
});
|
||||
const {
|
||||
data: deleteHistory,
|
||||
isLoading,
|
||||
refetch
|
||||
} = useGetDeleteHistoryQuery({
|
||||
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
|
||||
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
|
||||
search: debouncedSearchTerm,
|
||||
},
|
||||
{
|
||||
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
|
||||
})
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString('en-GB', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
});
|
||||
};
|
||||
|
||||
// Use useEffect to refetch data when the component mounts
|
||||
useEffect(() => {
|
||||
refetch();
|
||||
}, [refetch]);
|
||||
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr No.",
|
||||
"Date",
|
||||
"Distribution Amount",
|
||||
"Charges (USD)",
|
||||
"Year",
|
||||
"Quater",
|
||||
"Action",
|
||||
"Request On",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Country",
|
||||
"Phone Number",
|
||||
"Status"
|
||||
];
|
||||
|
||||
const extractedArray = filteredData?.map((item, index) => ({
|
||||
const extractedArray = deleteHistory?.data?.rows?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr No.": (
|
||||
<Text
|
||||
@@ -83,7 +101,7 @@ const DeletionHistory = () => {
|
||||
{index + 1}.
|
||||
</Text>
|
||||
),
|
||||
"Date": (
|
||||
"Request On": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
@@ -91,10 +109,10 @@ const DeletionHistory = () => {
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{formatDate(item.date)}
|
||||
{formatDate(item.Requested_on)}
|
||||
</Text>
|
||||
),
|
||||
"Distribution Amount": (
|
||||
"Client ID": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
@@ -102,10 +120,10 @@ const DeletionHistory = () => {
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{item.Distribution}
|
||||
{item.clientId}
|
||||
</Text>
|
||||
),
|
||||
"Charges (USD)": (
|
||||
"First Name": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
@@ -113,11 +131,11 @@ const DeletionHistory = () => {
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{item.charge}
|
||||
{item.firstName}
|
||||
{/* {formatDate(item.charge)} */}
|
||||
</Text>
|
||||
),
|
||||
Year: (
|
||||
"Last Name": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
@@ -125,10 +143,10 @@ const DeletionHistory = () => {
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{item.year}
|
||||
{item.lastName}
|
||||
</Text>
|
||||
),
|
||||
Quater: (
|
||||
"Country": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
@@ -136,18 +154,31 @@ const DeletionHistory = () => {
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{item.quarter}
|
||||
{item.country}
|
||||
</Text>
|
||||
),
|
||||
Action: (
|
||||
<Box display={'flex'} justifyContent={'space-around'}>
|
||||
<Tooltip rounded={'sm'} fontSize={'xs'} label='Accept' bg='#fff' color={'green.500'} placement="left-start">
|
||||
<Button color="green.500" rounded={'sm'} size={'xs'}>
|
||||
<CheckIcon /></Button></Tooltip>
|
||||
<Tooltip rounded={'sm'} fontSize={'xs'} label='Reject' bg='#fff' color={'red.500'} placement="left-start">
|
||||
<Button color="red.500" rounded={'sm'} size={'xs'}>
|
||||
<CloseIcon /></Button></Tooltip>
|
||||
</Box>
|
||||
"Phone Number": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{item.phoneNumber}
|
||||
</Text>
|
||||
),
|
||||
"Status": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
as={"span"}
|
||||
color={item?.deletionStatus? "red.500": "blue.500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
|
||||
fontWeight={'600'}
|
||||
>
|
||||
{item.deletionStatus}
|
||||
</Text>
|
||||
),
|
||||
}));
|
||||
|
||||
@@ -187,13 +218,13 @@ const DeletionHistory = () => {
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<HStack display={"flex"} alignItems={"center"}>
|
||||
{/* <HStack display={"flex"} alignItems={"center"}>
|
||||
<Pagination totalItems={10} />
|
||||
</HStack>
|
||||
</HStack> */}
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<DataTable
|
||||
<NormalTable
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
|
||||
@@ -6,15 +6,20 @@ import {
|
||||
HStack,
|
||||
Input,
|
||||
Text,
|
||||
Tooltip,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
||||
import DataTable from "../../Components/DataTable/DataTable";
|
||||
import NormalTable from "../../Components/DataTable/NormalTable";
|
||||
import Pagination from "../../Components/Pagination";
|
||||
import GlobalStateContext from "../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../Components/CustomAlertDialog";
|
||||
import { formatDate } from "../../Components/Functions/UTCConvertor";
|
||||
import { CheckIcon, CloseIcon } from "@chakra-ui/icons";
|
||||
import DeletionRequestApprove from "./DeletionRequestApprove";
|
||||
import { useGetDeleteRequestQuery } from "../../Services/delete.request.service";
|
||||
// import { formatDate } from "../../Components/Functions/UTCConvertor";
|
||||
|
||||
const DeletionRequest = () => {
|
||||
@@ -28,6 +33,32 @@ const DeletionRequest = () => {
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
const {
|
||||
data,
|
||||
isLoading: DeletionLoading
|
||||
} = useGetDeleteRequestQuery()
|
||||
|
||||
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
onOpen: onConfirmOpen,
|
||||
onClose: onConfirmClose,
|
||||
} = useDisclosure();
|
||||
|
||||
const {
|
||||
isOpen: isRejectOpen,
|
||||
onOpen: onRejectOpen,
|
||||
onClose: onRejectClose,
|
||||
} = useDisclosure();
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
@@ -39,9 +70,9 @@ const DeletionRequest = () => {
|
||||
}, []);
|
||||
|
||||
// ====================================================[Table Filter]================================================================
|
||||
const filteredData = deleteRequest.filter((item) => {
|
||||
const filteredData = data?.data?.rows?.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item.Distribution;
|
||||
const name = item?.firstName;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
|
||||
@@ -60,19 +91,21 @@ const DeletionRequest = () => {
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr No.",
|
||||
"Date",
|
||||
"Distribution Amount",
|
||||
"Charges (USD)",
|
||||
"Year",
|
||||
"Quater",
|
||||
"Amount",
|
||||
"Requested on",
|
||||
"Client ID",
|
||||
"First name",
|
||||
"Last name",
|
||||
"Country",
|
||||
"Phone number",
|
||||
"Status",
|
||||
"Action"
|
||||
];
|
||||
|
||||
const extractedArray = filteredData?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr No.": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
@@ -81,74 +114,112 @@ const DeletionRequest = () => {
|
||||
{index + 1}.
|
||||
</Text>
|
||||
),
|
||||
"Date": (
|
||||
"Requested on": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{formatDate(item.date)}
|
||||
{formatDate(item?.Requested_on)}
|
||||
</Text>
|
||||
),
|
||||
"Distribution Amount": (
|
||||
"Client ID": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{item.Distribution}
|
||||
{item?.clientId}
|
||||
</Text>
|
||||
),
|
||||
"Charges (USD)": (
|
||||
"First name": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{item.charge}
|
||||
{item?.firstName}
|
||||
{/* {formatDate(item.charge)} */}
|
||||
</Text>
|
||||
),
|
||||
Year: (
|
||||
"Last name": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{item.year}
|
||||
{item?.lastName}
|
||||
</Text>
|
||||
),
|
||||
Quater: (
|
||||
Country: (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{item.quater}
|
||||
{item?.country}
|
||||
</Text>
|
||||
),
|
||||
|
||||
Amount: (
|
||||
"Phone number": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
fontWeight={'500'}
|
||||
>
|
||||
{item.amount}
|
||||
{item?.phoneNumber}
|
||||
</Text>
|
||||
),
|
||||
Status:(<Box w={"auto"} display={'flex'} alignItems={'center'} isTruncated={true}>
|
||||
<Text
|
||||
as={'span'}
|
||||
fontWeight={"700"}
|
||||
textTransform={"none"}
|
||||
color={item.kycStatus ? "blue.500" : "red.500"}
|
||||
// px={2}
|
||||
py={0.5}
|
||||
variant={'solid'}
|
||||
|
||||
>
|
||||
{item.KYCStatus ? "Completed" : "Not complete"}
|
||||
</Text>
|
||||
</Box>),
|
||||
|
||||
Action: (
|
||||
<Box display={"flex"} justifyContent={"center"} gap={2}>
|
||||
<Button
|
||||
// colorScheme="forestGreen"
|
||||
// color="green.500"
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={2}
|
||||
py={1}
|
||||
onClick={() => {
|
||||
setActionId(item.id);
|
||||
onConfirmOpen();
|
||||
}}
|
||||
colorScheme="green"
|
||||
variant={"outline"}
|
||||
cursor={"pointer"}
|
||||
>
|
||||
Review
|
||||
</Button>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
@@ -187,17 +258,17 @@ const DeletionRequest = () => {
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<HStack display={"flex"} alignItems={"center"}>
|
||||
{/* <HStack display={"flex"} alignItems={"center"}>
|
||||
<Pagination totalItems={10} />
|
||||
</HStack>
|
||||
</HStack> */}
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<DataTable
|
||||
<NormalTable
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
isLoading={DeletionLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
// totalPages={10}
|
||||
@@ -213,6 +284,22 @@ const DeletionRequest = () => {
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
|
||||
|
||||
<DeletionRequestApprove
|
||||
data={deleteRequest}
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
id={actionId}
|
||||
// firstField={firstField}
|
||||
/>
|
||||
|
||||
|
||||
{/* <DepositRequestReject
|
||||
isOpen={isRejectOpen}
|
||||
onClose={onRejectClose}
|
||||
id={actionId}
|
||||
/> */}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
207
src/Pages/AccountDeletion/DeletionRequestApprove.jsx
Normal file
207
src/Pages/AccountDeletion/DeletionRequestApprove.jsx
Normal file
@@ -0,0 +1,207 @@
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useGetDepositRequestByIdQuery, useGetDepositRequestQuery, useUpdateDepositRequestMutation } from "../../Services/deposit.request.service";
|
||||
import FullscreenLoaders from "../../Components/Loaders/FullscreenLoaders";
|
||||
import ToastBox from "../../Components/ToastBox";
|
||||
import { useGetDrawalRequestQuery } from "../../Services/drawal.request.service";
|
||||
import { useApproveDepositRequestMutation, useGetDeleteRequestByIdQuery } from "../../Services/delete.request.service";
|
||||
|
||||
const FILE_TYPES = ["image/jpeg", "image/png", "image/gif"];
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
adminComment: yup.string().notRequired(),
|
||||
});
|
||||
|
||||
const DeletionRequestApprove = ({ isOpen, onClose, firstField, id, data:requestData }) => {
|
||||
const toast = useToast()
|
||||
const [file, setFile] = useState();
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
const [isBtnLoadingReject , setIsBtnLoadingReject] = useState(false)
|
||||
const [isReject , setIsReject] = useState(false)
|
||||
|
||||
const fileredData = requestData?.find((item)=> item?.id === id)
|
||||
const [ updateApproveRequest ] = useApproveDepositRequestMutation()
|
||||
const { data, isLoading } = useGetDeleteRequestByIdQuery(id, {
|
||||
skip: !id,
|
||||
});
|
||||
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
reset({
|
||||
comment:fileredData?.comment
|
||||
})
|
||||
|
||||
|
||||
}, [requestData, id])
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
setIsBtnLoading(isReject?false:true)
|
||||
setIsBtnLoadingReject(isReject)
|
||||
const approveReq = {
|
||||
adminComment:data?.adminComment,
|
||||
deletionStatus: isReject?"Reject": "Approved"
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await updateApproveRequest({ id ,data:approveReq })
|
||||
|
||||
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.error?.data?.message} status={"error"} />
|
||||
),
|
||||
});
|
||||
heandleOnClose()
|
||||
}else if(res?.data?.statusCode === 200) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.data?.message} />
|
||||
),
|
||||
});
|
||||
heandleOnClose()
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const onReject = () => {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
reset({
|
||||
comment: data?.data?.comment,
|
||||
});
|
||||
}
|
||||
}, [data, reset]);
|
||||
|
||||
const heandleOnClose = () =>{
|
||||
reset()
|
||||
onClose()
|
||||
setIsBtnLoading(false)
|
||||
setIsReject(false)
|
||||
setIsBtnLoadingReject(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
|
||||
<ModalOverlay />
|
||||
|
||||
<ModalContent pb={4}>
|
||||
{/* <ModalHeader fontSize={"md"}>Confirm</ModalHeader> */}
|
||||
<ModalCloseButton />
|
||||
{isLoading ? (
|
||||
<FullscreenLoaders height={"50vh"} />
|
||||
) : (
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mt={6} mb={4}>
|
||||
<FormLabel fontSize="sm">Investor Comment <Badge colorScheme="green">{fileredData?.currencyCode}</Badge></FormLabel>
|
||||
{/* <Textarea
|
||||
focusBorderColor="green.400"
|
||||
name="comment"
|
||||
{...register("comment")}
|
||||
fontSize="sm"
|
||||
type="text"
|
||||
size="sm"
|
||||
readOnly
|
||||
rows={5}
|
||||
/>
|
||||
{errors.comment && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.comment.message}
|
||||
</Text>
|
||||
)} */}
|
||||
|
||||
|
||||
<Text fontSize="sm" fontWeight={500} color={'gray.600'}>{data?.data?.comment}</Text>
|
||||
</FormControl>
|
||||
<FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">Admin Comment</FormLabel>
|
||||
<Textarea
|
||||
rows={5}
|
||||
focusBorderColor="green.400"
|
||||
name="adminComment"
|
||||
{...register("adminComment")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="sm"
|
||||
placeholder={"Enter your comments...."}
|
||||
resize={"none"}
|
||||
/>
|
||||
{errors.adminComment && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.adminComment.message}
|
||||
</Text>
|
||||
)}
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
colorScheme="red"
|
||||
mr={3}
|
||||
type="submit"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
variant={'ghost'}
|
||||
onClick={()=> setIsReject(true)}
|
||||
isLoading={isBtnLoadingReject}
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
>
|
||||
Approve
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeletionRequestApprove;
|
||||
158
src/Pages/AccountDeletion/DeletionRequestReject.jsx
Normal file
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
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
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
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
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
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
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,342 @@
|
||||
import { Box, Image, Text } from "@chakra-ui/react"
|
||||
// import error from "../assets/Error.svg"
|
||||
import robot from "../../assets/robot.png"
|
||||
// import robot from "../assets/robot.png"
|
||||
const Notification = () => {
|
||||
return (
|
||||
|
||||
<Box
|
||||
h={'100vh'}
|
||||
display={'flex'}
|
||||
justifyContent={'center'}
|
||||
alignItems={'center'}
|
||||
flexDirection={'column'}
|
||||
gap={8}
|
||||
>
|
||||
<Image src={robot} w={"171px"} />
|
||||
{/* <Text color={'green.800'} as={'span'} fontSize={'small'}>The requested URL was not found on this server.</Text> */}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
Text,
|
||||
Tooltip,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import { useForm} from "react-hook-form";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import * as yup from "yup";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
||||
import FormInputMain from "../../Components/FormInputMain";
|
||||
import {
|
||||
useGetContactQuery,
|
||||
useSendNotificationMutation,
|
||||
useUpdateContactMutation,
|
||||
} from "../../Services/contact.service";
|
||||
import FullscreenLoaders from "../../Components/Loaders/FullscreenLoaders";
|
||||
import ToastBox from "../../Components/ToastBox";
|
||||
import NormalTable from "../../Components/DataTable/NormalTable";
|
||||
import GlobalStateContext from "../../Contexts/GlobalStateContext";
|
||||
import { useGetInvestorsQuery } from "../../Services/investor.details.service";
|
||||
import { TABLE_PAGINATION } from "../../Constants/Paginations";
|
||||
import { formatDate, generateSerialNumber } from "../../Constants/Constants";
|
||||
import { ViewIcon } from "@chakra-ui/icons";
|
||||
import { useGetUnbanInvestorQuery } from "../../Services/ban.investor.service";
|
||||
|
||||
export default Notification
|
||||
export const notification = yup.object().shape({
|
||||
title: yup
|
||||
.string()
|
||||
.required("Investment Name is required"),
|
||||
ManualDate: yup
|
||||
.date()
|
||||
.required("Manual Date is required")
|
||||
.typeError("Invalid date format"),
|
||||
ManualTime: yup
|
||||
.string()
|
||||
.required("Manual Time is required")
|
||||
.matches(
|
||||
/^([01]\d|2[0-3]):?([0-5]\d)$/,
|
||||
"Invalid time format, must be in HH:mm"
|
||||
),
|
||||
expectedReturn: yup
|
||||
.string()
|
||||
.required("Expected Return is required"),
|
||||
});
|
||||
|
||||
export const notificationNew = yup.object().shape({
|
||||
title: yup
|
||||
.string()
|
||||
.required("Investment Name is required"),
|
||||
message: yup
|
||||
.string()
|
||||
.required("Message is required"),
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
const Notification = () => {
|
||||
const toast = useToast();
|
||||
const navigate = useNavigate();
|
||||
const [form, setForm] = useState({});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [ selectedRadio, setSelectedRadio] = useState([])
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
|
||||
const {
|
||||
control,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(notificationNew),
|
||||
|
||||
defaultValues: {
|
||||
title: '',
|
||||
message: '',
|
||||
},
|
||||
});
|
||||
|
||||
console.log(errors);
|
||||
|
||||
const {
|
||||
data: contact,
|
||||
isLoading: contactLoading,
|
||||
error,
|
||||
} = useGetContactQuery();
|
||||
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
// const {
|
||||
// data: investorDetails,
|
||||
// isLoading: investorDetailsLoading,
|
||||
// // error,
|
||||
// } = useGetInvestorsQuery({ page: currentPage, size: pageSize });
|
||||
|
||||
|
||||
const {
|
||||
data : investorDetails,
|
||||
isLoading: investorDetailsLoading,
|
||||
refetch,
|
||||
} = useGetUnbanInvestorQuery({
|
||||
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
|
||||
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
|
||||
search: debouncedSearchTerm,
|
||||
},
|
||||
{
|
||||
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
|
||||
});;
|
||||
|
||||
|
||||
|
||||
console.log(investorDetails);
|
||||
|
||||
|
||||
const [sendNotification] = useSendNotificationMutation();
|
||||
|
||||
|
||||
if (contactLoading) {
|
||||
return <FullscreenLoaders />;
|
||||
}
|
||||
|
||||
const formFields = [
|
||||
{
|
||||
label: "Notification Header",
|
||||
placeHolder: " ",
|
||||
name: "title",
|
||||
type: "text",
|
||||
width:"100%",
|
||||
maxLength:100,
|
||||
helperText:`Maximum length should be 100 characters. You have entered ${watch()?.title?.length || 0} characters.`,
|
||||
isRequired: true,
|
||||
section: "Send Custom Push Notification",
|
||||
// value: contact?.phoneNumber || "",
|
||||
},
|
||||
{
|
||||
label: "Notification Message",
|
||||
placeHolder: " ",
|
||||
name: "message",
|
||||
width:"100%",
|
||||
type: "textarea",
|
||||
isRequired: true,
|
||||
maxLength:200,
|
||||
helperText:`Maximum length should be 200 characters. You have entered ${watch()?.message?.length || 0} characters.`,
|
||||
section: "Send Custom Push Notification",
|
||||
// value: contact?.phoneNumber || "",
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
const groupedFields = formFields.reduce((groups, field) => {
|
||||
const { section } = field;
|
||||
if (!groups[section]) {
|
||||
groups[section] = [];
|
||||
}
|
||||
groups[section].push(field);
|
||||
return groups;
|
||||
}, {});
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
|
||||
const dataToPass = {
|
||||
...data,
|
||||
principal_xid:selectedRadio
|
||||
}
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const res = await sendNotification(dataToPass);
|
||||
console.log(res);
|
||||
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false)
|
||||
}else if(res?.data){
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false)
|
||||
setSelectedRadio([])
|
||||
reset({
|
||||
title: '', // Resetting specific fields
|
||||
message: '',
|
||||
}); // Clears the form fields
|
||||
}else{
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={'error'} message={"Something went wrong"} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr N/O",
|
||||
"Date",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Country",
|
||||
"Phone Number",
|
||||
"E-mail ID",
|
||||
"KYC Status",
|
||||
];
|
||||
|
||||
|
||||
const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({
|
||||
id: item?.principal_xid,
|
||||
"Sr N/O": (
|
||||
<Text
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center fw-bold web-text-small"
|
||||
>
|
||||
{generateSerialNumber(idx,currentPage, pageSize )}
|
||||
</Text>
|
||||
),
|
||||
Date: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{formatDate(item?.date)}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Client ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.clientReference_id}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"First Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
Country: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.country}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.phoneNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"E-mail ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.emailAddress}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"KYC Status": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Badge
|
||||
fontWeight={"500"}
|
||||
textTransform={"none"}
|
||||
color={item?.KYCStatus === false ? "red" : "blue"}
|
||||
px={2}
|
||||
py={0.5}
|
||||
variant={'ghost'}
|
||||
>
|
||||
{item?.KYCStatus === true ? "Completed" : "Incompleted"}
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={14}>
|
||||
<FormInputMain
|
||||
groupedFields={groupedFields}
|
||||
control={control}
|
||||
errors={errors}
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
btnLoading={isLoading}
|
||||
>
|
||||
<Box overflow={'scroll'} h={'58vh'}>
|
||||
<NormalTable
|
||||
centered={true}
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
// isLoading={isLoading}
|
||||
setSelectedRadio={setSelectedRadio}
|
||||
selectedRadio={selectedRadio}
|
||||
showRadioButton={true}
|
||||
/>
|
||||
</Box>
|
||||
</FormInputMain>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Notification;
|
||||
|
||||
@@ -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;
|
||||
|
||||
245
src/Pages/ChangePassword.jsx
Normal file
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;
|
||||
@@ -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>
|
||||
|
||||
@@ -27,17 +27,24 @@ import {
|
||||
useDepositRejectMutation,
|
||||
useGetDepositHistoryQuery,
|
||||
} from "../../../Services/deposit.request.service";
|
||||
|
||||
import { ExternalLinkIcon } from "@chakra-ui/icons";
|
||||
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
|
||||
import { generateSerialNumber } from "../../../Constants/Constants";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString('en-GB', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
});
|
||||
}; // Simple date formatter
|
||||
|
||||
const 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);
|
||||
@@ -54,26 +61,51 @@ const DepositHistory = () => {
|
||||
// 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 {
|
||||
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",
|
||||
@@ -92,124 +124,130 @@ 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,33 +258,32 @@ 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>
|
||||
) : (
|
||||
""
|
||||
),
|
||||
}))
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const IOtype = investmentType.filter(
|
||||
@@ -293,7 +330,7 @@ const filteredData = data?.data?.rows
|
||||
/>
|
||||
|
||||
<HStack display={"flex"} alignItems={"center"}>
|
||||
<Pagination
|
||||
<Pagination
|
||||
isLoading={depositHistoryLoading}
|
||||
pageSize={pageSize}
|
||||
setPageSize={setPageSize}
|
||||
|
||||
303
src/Pages/EmailNotification/EmailNotification.jsx
Normal file
303
src/Pages/EmailNotification/EmailNotification.jsx
Normal file
@@ -0,0 +1,303 @@
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
HStack,
|
||||
Input,
|
||||
Text,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useState } from "react";
|
||||
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
||||
import NormalTable from "../../Components/DataTable/NormalTable";
|
||||
import { useGetUnbanInvestorQuery } from "../../Services/ban.investor.service";
|
||||
import { formatDate, generateSerialNumber } from "../../Constants/Constants";
|
||||
import { TABLE_PAGINATION } from "../../Constants/Paginations";
|
||||
import ReactQuill from "react-quill";
|
||||
import "react-quill/dist/quill.snow.css"; // Importing the Quill snow theme
|
||||
import { useSendCustomEmailMutation } from "../../Services/contact.service";
|
||||
import { EmailIcon } from "@chakra-ui/icons";
|
||||
import ToastBox from "../../Components/ToastBox";
|
||||
|
||||
const EmailNotification = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [selectedRadio, setSelectedRadio] = useState([]);
|
||||
const [subject, setSubject] = useState("");
|
||||
const [value, setValue] = useState(""); // Quill content (body)
|
||||
const toast = useToast();
|
||||
|
||||
const [sendCustomNotification] = useSendCustomEmailMutation();
|
||||
|
||||
// ===========================[Table Setup]==============================
|
||||
const tableHeadRow = [
|
||||
"Sr N/O",
|
||||
"Date",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Country",
|
||||
"Phone Number",
|
||||
"E-mail ID",
|
||||
"KYC Status",
|
||||
];
|
||||
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
|
||||
const {
|
||||
data: investorDetails,
|
||||
isLoading: investorDetailsLoading,
|
||||
refetch,
|
||||
} = useGetUnbanInvestorQuery({
|
||||
page: currentPage, // Omit pagination for search
|
||||
size: 10000, // Omit pagination for search
|
||||
});
|
||||
|
||||
const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({
|
||||
id: item?.principal_xid,
|
||||
"Sr N/O": (
|
||||
<Text
|
||||
justifyContent={"left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center fw-bold web-text-small"
|
||||
>
|
||||
{generateSerialNumber(idx, currentPage, pageSize)}
|
||||
</Text>
|
||||
),
|
||||
Date: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{formatDate(item?.date)}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Client ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.clientReference_id}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"First Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
Country: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.country}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.phoneNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"E-mail ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item?.emailAddress}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"KYC Status": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Badge
|
||||
fontWeight={"500"}
|
||||
textTransform={"none"}
|
||||
color={item?.KYCStatus === false ? "red" : "blue"}
|
||||
px={2}
|
||||
py={0.5}
|
||||
variant={"ghost"}
|
||||
>
|
||||
{item?.KYCStatus === true ? "Completed" : "Incompleted"}
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const modules = {
|
||||
toolbar: [
|
||||
// [{ header: "1" }, { header: "2" },
|
||||
// // { font: [] }
|
||||
// ],
|
||||
// [{ size: [] }],
|
||||
["bold", "italic", "underline", "strike", "blockquote"],
|
||||
[{ list: "ordered" }, { list: "bullet" }],
|
||||
["clean"],
|
||||
],
|
||||
};
|
||||
|
||||
// Main submission logic
|
||||
const handleSend = async (e) => {
|
||||
e.preventDefault(); // Prevent default form submission
|
||||
|
||||
if (!subject || !value) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={"Subject or email body cannot be empty"} />
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedRadio.length === 0) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={"Please select at least one recipient"} />
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
const emailPayload = {
|
||||
subject,
|
||||
body: value,
|
||||
principal_xid: selectedRadio,
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
const res = await sendCustomNotification(emailPayload)
|
||||
console.log(res);
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false)
|
||||
}else if(res?.data){
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false)
|
||||
setSubject("")
|
||||
setValue("")
|
||||
setSelectedRadio([])
|
||||
|
||||
}else{
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={'error'} message={"Something went wrong"} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
as={"form"}
|
||||
{...OPACITY_ON_LOAD}
|
||||
overflowY={"scroll"}
|
||||
height={"100vh"}
|
||||
pb={14}
|
||||
pt={4}
|
||||
>
|
||||
<FormControl mb={0}>
|
||||
{/* <HStack
|
||||
py={4}
|
||||
pb={3}
|
||||
w={"100%"}
|
||||
justifyContent={"space-between"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Text as={"span"} fontSize={"sm"}>
|
||||
Customize your email
|
||||
</Text>
|
||||
</HStack> */}
|
||||
|
||||
<FormControl isRequired mb={3} p={1}>
|
||||
<FormLabel fontSize={"sm"}>Subject</FormLabel>
|
||||
<Input
|
||||
size={"md"}
|
||||
value={subject}
|
||||
onChange={(e) => setSubject(e.target.value)}
|
||||
focusBorderColor="forestGreen.300"
|
||||
rounded={0.5}
|
||||
type="text"
|
||||
/>
|
||||
{/* <FormHelperText>Entered subject will be reflected on emails subject body.</FormHelperText> */}
|
||||
</FormControl>
|
||||
|
||||
|
||||
<FormControl minH={400} isRequired mb={3} p={1}>
|
||||
<FormLabel fontSize={"sm"}>Create Custom body</FormLabel>
|
||||
<ReactQuill
|
||||
theme="snow"
|
||||
style={{
|
||||
height:300
|
||||
}}
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
modules={modules}
|
||||
placeholder="Start typing here..."
|
||||
|
||||
/>
|
||||
</FormControl>
|
||||
{/* <FormHelperText fontSize={"xs"}>
|
||||
We'll never share your email.
|
||||
</FormHelperText> */}
|
||||
|
||||
|
||||
|
||||
|
||||
</FormControl>
|
||||
|
||||
<Box overflow={'scroll'} h={'58vh'}>
|
||||
<NormalTable
|
||||
centered={true}
|
||||
emptyMessage={`We don't have any Sponsors`}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
setSelectedRadio={setSelectedRadio}
|
||||
selectedRadio={selectedRadio}
|
||||
showRadioButton={true}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<HStack justifyContent={"flex-end"} px={2}>
|
||||
<Button
|
||||
rightIcon={<EmailIcon />}
|
||||
rounded={"sm"}
|
||||
size={"sm"}
|
||||
colorScheme="forestGreen"
|
||||
type="submit"
|
||||
isLoading={isLoading}
|
||||
onClick={handleSend}
|
||||
>
|
||||
Send Email
|
||||
</Button>
|
||||
</HStack>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmailNotification;
|
||||
440
src/Pages/Fawateer/CreateRequest.jsx
Normal file
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;
|
||||
@@ -18,9 +18,11 @@ import {
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useState,useRef } from "react";
|
||||
import React, { useContext, useEffect, useState, useRef } from "react";
|
||||
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
||||
import NormalTable from "../../Components/DataTable/NormalTable";
|
||||
import { HiDotsVertical } from "react-icons/hi";
|
||||
import { Link, Link as RouterLink,useNavigate } from "react-router-dom";
|
||||
import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
|
||||
import {
|
||||
AddIcon,
|
||||
DeleteIcon,
|
||||
@@ -28,30 +30,68 @@ import {
|
||||
EmailIcon,
|
||||
ViewIcon,
|
||||
} from "@chakra-ui/icons";
|
||||
import GlobalStateContext from "../../Contexts/GlobalStateContext";
|
||||
import { debounce } from "../Master/Sponser/AddSponser";
|
||||
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
||||
import DataTable from "../../Components/DataTable/DataTable";
|
||||
import CustomAlertDialog from "../../Components/CustomAlertDialog";
|
||||
import Pagination from "../../Components/Pagination";
|
||||
import GlobalStateContext from "../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../Components/CustomAlertDialog";
|
||||
import ToastBox from "../../Components/ToastBox";
|
||||
import { debounce } from "../Master/Sponser/AddSponser";
|
||||
// import InvestmentDetailsEdit from "./InvestmentDetailsEdit";
|
||||
import { useGetInvestorsQuery } from "../../Services/investor.details.service";
|
||||
import { TABLE_PAGINATION } from "../../Constants/Paginations";
|
||||
import { exportToExcel, generateSerialNumber } from "../../Constants/Constants";
|
||||
import { LuFileSpreadsheet } from "react-icons/lu";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
|
||||
|
||||
const BankInvestor = () => {
|
||||
const FawateerRequest = () => {
|
||||
const navigate = useNavigate();
|
||||
const toast = useToast();
|
||||
const thirdField = useRef();
|
||||
const { bankInvestor, setBankInvestor, slideFromRight } =
|
||||
const { InvestorDetails, setInvestorDetails, slideFromRight } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
const {isOpen: isEditOpen,onOpen: onEditOpen,onClose: onEditClose,} = useDisclosure();
|
||||
const btnRef = React.useRef()
|
||||
const {
|
||||
isOpen: isEditOpen,
|
||||
onOpen: onEditOpen,
|
||||
onClose: onEditClose,
|
||||
} = useDisclosure();
|
||||
const btnRef = React.useRef();
|
||||
|
||||
|
||||
// =========================== [Use State] =============================
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
|
||||
// Debounce the search term to avoid making a request on every keystroke
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedSearchTerm(searchTerm);
|
||||
}, 500); // Adjust delay as needed
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [searchTerm]);
|
||||
|
||||
|
||||
const {
|
||||
data: investorDetails,
|
||||
isLoading: investorDetailsLoading,
|
||||
error,
|
||||
} = useGetInvestorsQuery({
|
||||
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
|
||||
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
|
||||
search: debouncedSearchTerm,
|
||||
},
|
||||
{
|
||||
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
@@ -65,21 +105,22 @@ const BankInvestor = () => {
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr N/O",
|
||||
"Sr No",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Country",
|
||||
"Country",
|
||||
"Phone Number",
|
||||
"E-mail ID",
|
||||
"KYC Status",
|
||||
"Action",
|
||||
// "Type",
|
||||
// "KYC Status",
|
||||
"Approval Status",
|
||||
];
|
||||
|
||||
const handleUpdateStatus = debounce((id) => {
|
||||
setBankInvestor((prevData) =>
|
||||
prevData.map((bankInvestor) =>
|
||||
bankInvestor.id === id ? { ...bankInvestor } : bankInvestor
|
||||
setInvestorDetails((prevData) =>
|
||||
prevData.map((InvestorDetails) =>
|
||||
InvestorDetails.id === id ? { ...InvestorDetails } : InvestorDetails
|
||||
)
|
||||
);
|
||||
toast({
|
||||
@@ -88,9 +129,9 @@ const BankInvestor = () => {
|
||||
}, 300);
|
||||
|
||||
// ====================================================[Table Filter]================================================================
|
||||
const filteredData = bankInvestor?.filter((item) => {
|
||||
const filteredData = investorDetails?.data?.rows?.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item.firstName;
|
||||
const name = [item?.principal?.firstName, item?.principal?.lastName, item?.country?.countryName, item?.principal?.mobileNumber, item?.principal?.emailAddress].filter(Boolean).join(' ');
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name?.toLowerCase().includes(searchLower);
|
||||
|
||||
@@ -106,74 +147,100 @@ const BankInvestor = () => {
|
||||
return nameMatches;
|
||||
});
|
||||
|
||||
const extractedArray = filteredData?.map((item) => ({
|
||||
|
||||
|
||||
const customHeaders = [
|
||||
{ label: "ID", key: "id" },
|
||||
{ label: "Client ID", key: "clientReference_id" },
|
||||
{ label: "First Name", key: "principal.firstName" }, // Nested property
|
||||
{ label: "Last Name", key: "principal.lastName" }, // Nested property
|
||||
{ label: "Country", key: "country.countryName" }, // Nested property
|
||||
{ label: "Phone Number", key: "principal.mobileNumber" }, // Nested property
|
||||
{ label: "E-mail ID", key: "principal.emailAddress" }, // Nested property
|
||||
{ label: "Type", key: "investor_type.investorTypeName" }, // Nested property
|
||||
{ label: "Status", key: "ioStatus" }, // Simple property
|
||||
{ label: "KYC Status", key: "KYCStatus" }, // Simple property
|
||||
];
|
||||
|
||||
|
||||
const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({
|
||||
id: item?.id,
|
||||
"Sr N/O": (
|
||||
"Sr No": (
|
||||
<Text
|
||||
w={'24px'}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
className="d-flex align-items-center fw-bold web-text-small"
|
||||
>
|
||||
{item.id}
|
||||
{/* {item.id} */}
|
||||
{generateSerialNumber(idx,currentPage, pageSize )}
|
||||
|
||||
</Text>
|
||||
),
|
||||
"Client ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.clientId}
|
||||
{item.clientReference_id}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
),
|
||||
"First Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.firstName}
|
||||
{item?.principal?.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Box w={"70px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.lastName}
|
||||
{item?.principal?.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
Country: (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.country}
|
||||
{item?.country?.countryName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.phoneNumber}
|
||||
{item?.principal?.mobileNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"E-mail ID": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.mailId}
|
||||
{item?.principal?.emailAddress}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"KYC Status": (
|
||||
"Type": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Badge fontWeight={'500'} textTransform={'none'} color={item.status === "Completed" ? "blue" : "red"} px={2} py={0.5}>
|
||||
{item.status}
|
||||
</Badge>
|
||||
<Text as={"span"} >
|
||||
<Badge color={"forestGreen.500"} variant={'ghost'} fontWeight={"700"} px={2} py={0.5}>
|
||||
{item?.investor_type?.investorTypeName}
|
||||
</Badge>
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
Action: (
|
||||
<Switch
|
||||
size={"sm"}
|
||||
colorScheme="forestGreen"
|
||||
onChange={() => handleUpdateStatus(item.id)}
|
||||
isChecked={item.status}
|
||||
/>
|
||||
"Approval Status": (
|
||||
<Box w={"auto"} isTruncated={true}>
|
||||
<Badge
|
||||
fontWeight={"700"}
|
||||
textTransform={"none"}
|
||||
colorScheme={item.ioStatus ? "red" : "purple"}
|
||||
px={2}
|
||||
py={0.5}
|
||||
>
|
||||
Approved
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
@@ -195,6 +262,10 @@ const BankInvestor = () => {
|
||||
onEditOpen();
|
||||
};
|
||||
|
||||
console.log(investorDetails?.data?.totalItems);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
|
||||
<Box bg="white.500">
|
||||
@@ -208,7 +279,7 @@ const BankInvestor = () => {
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
mt={1}
|
||||
mt={1}
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
@@ -218,17 +289,45 @@ const BankInvestor = () => {
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
|
||||
|
||||
<HStack display={"flex"} alignItems={"center"}>
|
||||
<Pagination totalItems={10} />
|
||||
|
||||
|
||||
<Pagination
|
||||
isLoading={investorDetailsLoading}
|
||||
pageSize={pageSize}
|
||||
setPageSize={setPageSize}
|
||||
currentPage={currentPage}
|
||||
setCurrentPage={setCurrentPage}
|
||||
totalItems={investorDetails?.data?.totalItems}
|
||||
|
||||
/>
|
||||
|
||||
|
||||
{/*
|
||||
<Button
|
||||
leftIcon={<AddIcon />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
onClick={()=> navigate('create-request')}
|
||||
>
|
||||
Create request
|
||||
</Button> */}
|
||||
|
||||
</HStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<DataTable
|
||||
<NormalTable
|
||||
// centered={true}
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
isLoading={investorDetailsLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
@@ -246,4 +345,4 @@ const BankInvestor = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default BankInvestor;
|
||||
export default FawateerRequest
|
||||
247
src/Pages/Fawateer/SelectInvestorModal.jsx
Normal file
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,336 @@
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Link,
|
||||
Text,
|
||||
Tooltip,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { CheckIcon, CloseIcon, ExternalLinkIcon } from "@chakra-ui/icons";
|
||||
import Pagination from "../../../Components/Pagination";
|
||||
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
|
||||
import DrawalRequestReject from "../../WithDrawal/DrawalRequest/DrawalRequestReject";
|
||||
import NormalTable from "../../../Components/DataTable/NormalTable";
|
||||
import DrawalRequestApprove from "../../WithDrawal/DrawalRequest/DrawalRequestApprove";
|
||||
import { generateSerialNumber } from "../../../Constants/Constants";
|
||||
import {
|
||||
useGetApproveHistoryQuery,
|
||||
useGetFawateerRequestQuery,
|
||||
} from "../../../Services/fawateer.request.service";
|
||||
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
|
||||
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
|
||||
|
||||
const ApproveHistory = () => {
|
||||
const toast = useToast();
|
||||
const { slideFromRight, approveHistory, setApproveHistory } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
onOpen: onConfirmOpen,
|
||||
onClose: onConfirmClose,
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isRejectOpen,
|
||||
onOpen: onRejectOpen,
|
||||
onClose: onRejectClose,
|
||||
} = useDisclosure();
|
||||
|
||||
const {
|
||||
data,
|
||||
isLoading: drawalRequestLoading,
|
||||
error,
|
||||
refetch,
|
||||
} = useGetApproveHistoryQuery();
|
||||
|
||||
console.log(data?.data?.rows);
|
||||
|
||||
// Use useEffect to refetch data when the component mounts
|
||||
useEffect(() => {
|
||||
refetch();
|
||||
}, [refetch]);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
// ====================================================[Table Filter]================================================================
|
||||
const filteredData = data?.data?.rows?.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item.firstName;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
|
||||
// Filter by status
|
||||
// const status = item.status;
|
||||
// const statusLower = status ? "active" : "inactive";
|
||||
|
||||
// const statusMatches =
|
||||
// statusFilter === "all" ||
|
||||
// (statusFilter === "active" && status === true) ||
|
||||
// (statusFilter === "inactive" && status === false);
|
||||
|
||||
return nameMatches;
|
||||
});
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr.no",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"E-mail ID",
|
||||
"Phone Number",
|
||||
"Deposit Date",
|
||||
"Deposit Amount (BHD)",
|
||||
"Support Image",
|
||||
"Status",
|
||||
];
|
||||
|
||||
const extractedArray = filteredData?.map((item, idx) => ({
|
||||
// id: item?.id,
|
||||
"Sr.no": (
|
||||
<Text
|
||||
w={"auto"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{generateSerialNumber(idx, currentPage, pageSize)}
|
||||
</Text>
|
||||
),
|
||||
"Client ID": (
|
||||
<Text
|
||||
w={"60px"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.clientReference_id}
|
||||
</Text>
|
||||
),
|
||||
"First Name": (
|
||||
<Box isTruncated={true} w={"80px"}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"50px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"E-mail ID": (
|
||||
<Box isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.emailAddress}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"100px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.mobileNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Deposit Date": (
|
||||
<Box
|
||||
w={"100px"}
|
||||
isTruncated={true}
|
||||
display={"flex"}
|
||||
>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{formatDate(item?.transaction_date)}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Deposit Amount (BHD)": (
|
||||
<Box w={"130px"} isTruncated={true} display={"flex"}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{/* {item.investorAmount} */}
|
||||
{parseFloat(item?.transaction_amount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
{/* <Badge ms={1} colorScheme="green">{item?.transaction_amount}</Badge> */}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Support Image": (
|
||||
<Text
|
||||
color={"green.500"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item?.spportFile_path&&<Badge
|
||||
px={2}
|
||||
py={0.5}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
colorScheme={"forestGreen"}
|
||||
>
|
||||
<Link
|
||||
href={import.meta.env.VITE_IMAGE_URL + item?.spportFile_path}
|
||||
isExternal
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Box me={"1px"}
|
||||
as="span"
|
||||
cursor={"pointer"}
|
||||
>
|
||||
View
|
||||
</Box>
|
||||
<ExternalLinkIcon />
|
||||
</Link>
|
||||
</Badge>}
|
||||
</Text>
|
||||
),
|
||||
Status: (
|
||||
<Box isTruncated={true} display={"flex"}>
|
||||
<Badge
|
||||
my={1}
|
||||
fontWeight={500}
|
||||
px={2}
|
||||
py={"2px"}
|
||||
rounded={4}
|
||||
colorScheme={
|
||||
item?.transactionStatus === "Approved"
|
||||
? "green"
|
||||
: item?.transactionStatus === "Pending"
|
||||
? "yellow"
|
||||
: item?.transactionStatus === "Reject"
|
||||
? "red"
|
||||
: "gray" // default border color if status doesn't match any condition
|
||||
}
|
||||
>
|
||||
{item.transactionStatus}
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedSponsors = sponser.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setSponser(updatedSponsors);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
ps={1}
|
||||
pe={1}
|
||||
pb={4}
|
||||
pt={4}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<Pagination
|
||||
isLoading={drawalRequestLoading}
|
||||
pageSize={pageSize}
|
||||
setPageSize={setPageSize}
|
||||
currentPage={currentPage}
|
||||
setCurrentPage={setCurrentPage}
|
||||
totalItems={data?.data?.totalItems}
|
||||
/>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
isLoading={drawalRequestLoading}
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
// totalPages={10}
|
||||
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<DrawalRequestApprove
|
||||
// data={data?.data?.rows}
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
id={actionId}
|
||||
// firstField={firstField}
|
||||
/>
|
||||
<DrawalRequestReject
|
||||
isOpen={isRejectOpen}
|
||||
onClose={onRejectClose}
|
||||
id={actionId}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApproveHistory;
|
||||
358
src/Pages/FawateerChecker/ApproveHistory/ApproveHistoryMaker.jsx
Normal file
358
src/Pages/FawateerChecker/ApproveHistory/ApproveHistoryMaker.jsx
Normal file
@@ -0,0 +1,358 @@
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Link,
|
||||
Text,
|
||||
Tooltip,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { CheckIcon, CloseIcon, ExternalLinkIcon } from "@chakra-ui/icons";
|
||||
import Pagination from "../../../Components/Pagination";
|
||||
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
|
||||
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
|
||||
import DrawalRequestReject from "../../WithDrawal/DrawalRequest/DrawalRequestReject";
|
||||
import NormalTable from "../../../Components/DataTable/NormalTable";
|
||||
import DrawalRequestApprove from "../../WithDrawal/DrawalRequest/DrawalRequestApprove";
|
||||
import { generateSerialNumber } from "../../../Constants/Constants";
|
||||
import {
|
||||
useGetApproveHistoryQuery,
|
||||
useGetFawateerForMakerRequestQuery,
|
||||
useGetFawateerRequestQuery,
|
||||
} from "../../../Services/fawateer.request.service";
|
||||
import { TABLE_PAGINATION } from "../../../Constants/Paginations";
|
||||
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
|
||||
|
||||
const ApproveHistoryMaker = () => {
|
||||
const toast = useToast();
|
||||
const { slideFromRight, approveHistory, setApproveHistory } =
|
||||
useContext(GlobalStateContext);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [deleteAlert, setDeleteAlert] = useState(false);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
const [mouseEntered, setMouseEntered] = useState(false);
|
||||
const [mouseEnteredId, setMouseEnteredId] = useState("");
|
||||
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
|
||||
|
||||
const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size);
|
||||
const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page);
|
||||
|
||||
const formatDate = (date) => {
|
||||
return new Date(date).toLocaleDateString("en-GB", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
onOpen: onConfirmOpen,
|
||||
onClose: onConfirmClose,
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isRejectOpen,
|
||||
onOpen: onRejectOpen,
|
||||
onClose: onRejectClose,
|
||||
} = useDisclosure();
|
||||
|
||||
const {
|
||||
data,
|
||||
isLoading: drawalRequestLoading,
|
||||
error,
|
||||
refetch
|
||||
} = useGetFawateerForMakerRequestQuery(
|
||||
{
|
||||
page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search
|
||||
size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search
|
||||
searchTerm: debouncedSearchTerm,
|
||||
},
|
||||
{
|
||||
skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request
|
||||
}
|
||||
);
|
||||
|
||||
console.log(data);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedSearchTerm(searchTerm);
|
||||
}, 500); // Adjust delay as needed
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [searchTerm]);
|
||||
|
||||
// Use useEffect to refetch data when the component mounts
|
||||
useEffect(() => {
|
||||
refetch();
|
||||
}, [refetch]);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate loading
|
||||
const timer = setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 1500);
|
||||
|
||||
// Cleanup the timer on component unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
// ====================================================[Table Filter]================================================================
|
||||
const filteredData = data?.data?.rows?.filter((item) => {
|
||||
// Filter by name (case insensitive)
|
||||
const name = item.firstName;
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
|
||||
// Filter by status
|
||||
// const status = item.status;
|
||||
// const statusLower = status ? "active" : "inactive";
|
||||
|
||||
// const statusMatches =
|
||||
// statusFilter === "all" ||
|
||||
// (statusFilter === "active" && status === true) ||
|
||||
// (statusFilter === "inactive" && status === false);
|
||||
|
||||
return nameMatches;
|
||||
});
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr.no",
|
||||
"Client ID",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"E-mail ID",
|
||||
"Phone Number",
|
||||
"Deposit Date",
|
||||
"Deposit Amount (BHD)",
|
||||
"Support Image",
|
||||
"Status",
|
||||
];
|
||||
|
||||
const extractedArray = data?.data?.rows?.map((item, idx) => ({
|
||||
|
||||
// id: item?.id,
|
||||
"Sr.no": (
|
||||
<Text
|
||||
w={"auto"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{generateSerialNumber(idx, currentPage, pageSize)}
|
||||
</Text>
|
||||
),
|
||||
"Client ID": (
|
||||
<Text
|
||||
w={"60px"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.clientReference_id}
|
||||
</Text>
|
||||
),
|
||||
"First Name": (
|
||||
<Box isTruncated={true} w={"80px"}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box w={"50px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"E-mail ID": (
|
||||
<Box isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.emailAddress}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Phone Number": (
|
||||
<Box w={"100px"} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{item.mobileNumber}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Deposit Date": (
|
||||
<Box
|
||||
w={"100px"}
|
||||
isTruncated={true}
|
||||
display={"flex"}
|
||||
>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{formatDate(item?.transaction_date)}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Deposit Amount (BHD)": (
|
||||
<Box w={"130px"} isTruncated={true} display={"flex"}>
|
||||
<Text as={"span"} color={"teal.900"}>
|
||||
{/* {item.investorAmount} */}
|
||||
{parseFloat(item?.transaction_amount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
{/* <Badge ms={1} colorScheme="green">{item?.transaction_amount}</Badge> */}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Support Image": (
|
||||
<Text
|
||||
color={"green.500"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item?.spportFile_path&&<Badge
|
||||
px={2}
|
||||
py={0.5}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
colorScheme={"forestGreen"}
|
||||
>
|
||||
<Link
|
||||
href={import.meta.env.VITE_IMAGE_URL + item?.spportFile_path}
|
||||
isExternal
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Box me={"1px"}
|
||||
as="span"
|
||||
cursor={"pointer"}
|
||||
>
|
||||
View
|
||||
</Box>
|
||||
<ExternalLinkIcon />
|
||||
</Link>
|
||||
</Badge>}
|
||||
</Text>
|
||||
),
|
||||
Status: (
|
||||
<Box isTruncated={true} display={"flex"}>
|
||||
<Badge
|
||||
my={1}
|
||||
fontWeight={500}
|
||||
px={2}
|
||||
py={"2px"}
|
||||
rounded={4}
|
||||
colorScheme={
|
||||
item?.transactionStatus === "Approved"
|
||||
? "green"
|
||||
: item?.transactionStatus === "Pending"
|
||||
? "yellow"
|
||||
: item?.transactionStatus === "Reject"
|
||||
? "red"
|
||||
: "gray" // default border color if status doesn't match any condition
|
||||
}
|
||||
>
|
||||
{item.transactionStatus}
|
||||
</Badge>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedSponsors = sponser.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setSponser(updatedSponsors);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
ps={1}
|
||||
pe={1}
|
||||
pb={4}
|
||||
pt={4}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<Pagination
|
||||
isLoading={drawalRequestLoading}
|
||||
pageSize={pageSize}
|
||||
setPageSize={setPageSize}
|
||||
currentPage={currentPage}
|
||||
setCurrentPage={setCurrentPage}
|
||||
totalItems={data?.data?.totalItems}
|
||||
/>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
isLoading={drawalRequestLoading}
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
// totalPages={10}
|
||||
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<DrawalRequestApprove
|
||||
// data={data?.data?.rows}
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
id={actionId}
|
||||
// firstField={firstField}
|
||||
/>
|
||||
<DrawalRequestReject
|
||||
isOpen={isRejectOpen}
|
||||
onClose={onRejectClose}
|
||||
id={actionId}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApproveHistoryMaker;
|
||||
|
||||
95
src/Pages/FawateerChecker/ApproveHistory/ConfirmHistory.jsx
Normal file
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
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
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;
|
||||
|
||||
177
src/Pages/FawateerChecker/ApproveRequest/RequestApproveModal.jsx
Normal file
177
src/Pages/FawateerChecker/ApproveRequest/RequestApproveModal.jsx
Normal file
@@ -0,0 +1,177 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useDepositRejectMutation } from "../../../Services/deposit.request.service";
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
import { useApproveCommentMutation } from "../../../Services/fawateer.request.service";
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
// checkerComment: yup.string().required("Comment is required")
|
||||
// .max(50, "Investment name cannot be more than 50 characters"),
|
||||
checkerComment: yup
|
||||
.string()
|
||||
.required("Comment is required")
|
||||
.max(200, "Approve Comment cannot be more than 200 characters"),
|
||||
});
|
||||
|
||||
const RequestApproveModal = ({ isOpen, onClose, firstField ,id}) => {
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
const [ approveFawateer ] = useApproveCommentMutation()
|
||||
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
console.log(data, "tewxttttt");
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await approveFawateer({data,id})
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}else if(res?.data){
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.data?.message} />
|
||||
),
|
||||
});
|
||||
onClose()
|
||||
setIsBtnLoading(false)
|
||||
}else{
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={'error'} message={"Something went wrong"} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const selectedFile = event.target.files[0];
|
||||
setFile(selectedFile);
|
||||
};
|
||||
|
||||
|
||||
const { data, isLoading } =
|
||||
(id, {
|
||||
skip: !id,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
reset({
|
||||
investorAmount: data?.data?.investorAmount,
|
||||
});
|
||||
}
|
||||
}, [data, reset]);
|
||||
|
||||
const heandleOnClose = () =>{
|
||||
reset()
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
|
||||
<ModalOverlay />
|
||||
<ModalContent pb={4}>
|
||||
<ModalHeader fontSize={"md"}>Approve Comment</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
{isLoading ? (
|
||||
<FullscreenLoaders height={"50vh"} />
|
||||
) : (
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">Comment</FormLabel>
|
||||
<Textarea
|
||||
rows={6}
|
||||
focusBorderColor="green.400"
|
||||
name="checkerComment"
|
||||
{...register("checkerComment")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="md"
|
||||
placeholder={"Enter your checkerComment...."}
|
||||
rounded={"md"}
|
||||
resize={"none"}
|
||||
maxLength={200}
|
||||
/>
|
||||
{errors.checkerComment && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.checkerComment.message}
|
||||
</Text>
|
||||
)}
|
||||
<FormHelperText fontSize="xs" color="gray.500">
|
||||
Maximum length should be 200 characters. You have entered
|
||||
{watch("checkerComment")?.length || 0} characters.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
colorScheme="gray"
|
||||
mr={3}
|
||||
onClick={onClose}
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RequestApproveModal;
|
||||
165
src/Pages/FawateerChecker/ApproveRequest/RequestRejectModal.jsx
Normal file
165
src/Pages/FawateerChecker/ApproveRequest/RequestRejectModal.jsx
Normal file
@@ -0,0 +1,165 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useDepositRejectMutation } from "../../../Services/deposit.request.service";
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
import { useRejectCommentMutation } from "../../../Services/fawateer.request.service";
|
||||
|
||||
export const conformModalSchema = yup.object().shape({
|
||||
comments: yup.string().required("Comment is required"),
|
||||
});
|
||||
|
||||
const RequestRejectModal = ({ isOpen, onClose, firstField ,id}) => {
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
const [ rejectFawateer ] = useRejectCommentMutation()
|
||||
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
console.log(data, "tewxttttt");
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await rejectFawateer({data,id})
|
||||
if (res?.error) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}else if(res?.data){
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.data?.message} />
|
||||
),
|
||||
});
|
||||
onClose()
|
||||
setIsBtnLoading(false)
|
||||
}else{
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={'error'} message={"Something went wrong"} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const selectedFile = event.target.files[0];
|
||||
setFile(selectedFile);
|
||||
};
|
||||
|
||||
|
||||
const { data, isLoading } =
|
||||
(id, {
|
||||
skip: !id,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
reset({
|
||||
investorAmount: data?.data?.investorAmount,
|
||||
});
|
||||
}
|
||||
}, [data, reset]);
|
||||
|
||||
const heandleOnClose = () =>{
|
||||
reset()
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
|
||||
<ModalOverlay />
|
||||
<ModalContent pb={4}>
|
||||
<ModalHeader fontSize={"md"}>Reject Comment</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
{isLoading ? (
|
||||
<FullscreenLoaders height={"50vh"} />
|
||||
) : (
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">Comment</FormLabel>
|
||||
<Textarea
|
||||
rows={6}
|
||||
focusBorderColor="green.400"
|
||||
name="comments"
|
||||
{...register("comments")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="md"
|
||||
placeholder={"Enter your comments...."}
|
||||
rounded={"md"}
|
||||
resize={"none"}
|
||||
/>
|
||||
{errors.comments && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.comments.message}
|
||||
</Text>
|
||||
)}
|
||||
</FormControl>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
colorScheme="gray"
|
||||
mr={3}
|
||||
onClick={onClose}
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RequestRejectModal;
|
||||
138
src/Pages/ForgetPassword.jsx
Normal file
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"
|
||||
@@ -212,7 +217,7 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, da
|
||||
isOpen={alert}
|
||||
onClose={() => setAlert(false)}
|
||||
alertHandler={handleSave}
|
||||
message={"Are you sure you want to add this artifact?"}
|
||||
message={"Are you sure you want to update this artifact?"}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -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
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
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
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;
|
||||
146
src/Pages/IO_Management/CreateIO/IOCashDetails/IOCashDetails.jsx
Normal file
146
src/Pages/IO_Management/CreateIO/IOCashDetails/IOCashDetails.jsx
Normal file
@@ -0,0 +1,146 @@
|
||||
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() && (
|
||||
<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
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
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(IODetails?.ioCash || 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;
|
||||
});
|
||||
|
||||
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,9 +6,6 @@ import { useForm } from "react-hook-form";
|
||||
import * as yup from "yup";
|
||||
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
|
||||
import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
|
||||
import { generateUniqueId } from "../../../Contexts/GlobalStateProvider";
|
||||
import { useGetInvestmentTypesQuery } from "../../../Services/investment.type.service";
|
||||
import { useGetActiveSponserMasterQuery } from "../../../Services/sponser.service";
|
||||
import {
|
||||
useCreateIOMutation,
|
||||
useGetIOByIdQuery,
|
||||
@@ -16,25 +13,10 @@ import {
|
||||
} from "../../../Services/io.service";
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
import {
|
||||
Input,
|
||||
Table,
|
||||
Tbody,
|
||||
Td,
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import { formatDate } from "../../Master/Sponser/Sponsers";
|
||||
|
||||
import behrain from "../../../assets/bahrain_flag.png";
|
||||
import kuwait from "../../../assets/kuwait_flag.png";
|
||||
import oman from "../../../assets/oman_flag.png";
|
||||
import qatar from "../../../assets/qatar_flag.png";
|
||||
import uae from "../../../assets/uae_flag.png";
|
||||
import saudi from "../../../assets/saudi_arabia_flag.png";
|
||||
import { formatDatee } from "../../../Components/FormField";
|
||||
import { removeTrailingZeros } from "../../../Constants/Constants";
|
||||
import { formatDateToYYYYMMDD, removeTrailingZeros } from "../../../Constants/Constants";
|
||||
|
||||
const schema = yup.object().shape({
|
||||
investmentNameEnglish: yup
|
||||
@@ -43,7 +25,7 @@ const schema = yup.object().shape({
|
||||
.min(3, "IO name in English must be at least 3 characters long")
|
||||
.max(150, "IO name in English must be at most 150 characters long"),
|
||||
|
||||
investmentNameArabic: yup
|
||||
investmentNameArabic: yup
|
||||
.string()
|
||||
.required("IO name in Arabic is required")
|
||||
.min(3, "IO name in Arabic must be at least 3 characters long")
|
||||
@@ -69,7 +51,6 @@ const schema = yup.object().shape({
|
||||
.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")
|
||||
@@ -89,7 +70,7 @@ const schema = yup.object().shape({
|
||||
InvestmentDetails: yup.string().notRequired(),
|
||||
|
||||
comment: yup.string().notRequired()
|
||||
.min(10, "Comment must be at least 10 characters long")
|
||||
// .min(10, "Comment must be at least 10 characters long")
|
||||
.max(100, "Comment must be at most 100 characters long"),
|
||||
|
||||
expectedReturn: yup
|
||||
@@ -97,22 +78,29 @@ const schema = yup.object().shape({
|
||||
.required("Expected return is required"),
|
||||
});
|
||||
|
||||
|
||||
const IODetails = ({ enableNextTab, index, data }) => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const params = useParams();
|
||||
const navigate = useNavigate();
|
||||
const toast = useToast();
|
||||
|
||||
|
||||
const handleInputChange = (index, newValue) => {
|
||||
const handleInputChangeCreate = (index, newValue) => {
|
||||
|
||||
const updatedValues = [...values];
|
||||
updatedValues[index].value = newValue;
|
||||
setValues(updatedValues);
|
||||
console.log(values);
|
||||
};
|
||||
|
||||
const handleInputChangeEdit = (index, newValue) => {
|
||||
// Allow only whole numbers using regex
|
||||
if (/^\d*$/.test(newValue)) {
|
||||
const updatedValues = [...values];
|
||||
updatedValues[index].value = newValue;
|
||||
setValues(updatedValues);
|
||||
console.log(values);
|
||||
}
|
||||
};
|
||||
|
||||
// ======================[ States ]
|
||||
@@ -132,6 +120,7 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
isLoading: IObyIDisLoading,
|
||||
error: IObyIDerror,
|
||||
} = useGetIOByIdQuery(id, { skip: !id });
|
||||
console.log(IObyID);
|
||||
|
||||
const [creatIO] = useCreateIOMutation();
|
||||
const [updateIO] = useUpdateIOMutation();
|
||||
@@ -165,20 +154,88 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
const minInvestmentById = IObyID?.data?.minInvestmentAmt?.map(({minInvestmentAmt, country, country_xid, })=>{
|
||||
const minInvestmentById = IObyID?.data?.minInvestmentAmt?.map(({minInvestmentAmt, country, currencyCode, country_xid,id })=>{
|
||||
console.log(currencyCode);
|
||||
return{
|
||||
_id:id,
|
||||
id:country_xid,
|
||||
country: country?.countryName,
|
||||
value: removeTrailingZeros(minInvestmentAmt),
|
||||
logo: country?.flagIcon,
|
||||
curr: country?.countryCode,
|
||||
curr: currencyCode,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const schemaEdit = yup.object().shape({
|
||||
investmentNameEnglish: yup
|
||||
.string()
|
||||
.required("IO name in English is required")
|
||||
.min(3, "IO name in English must be at least 3 characters long")
|
||||
.max(150, "IO name in English must be at most 150 characters long"),
|
||||
|
||||
investmentNameArabic: yup
|
||||
.string()
|
||||
.required("IO name in Arabic is required")
|
||||
.min(3, "IO name in Arabic must be at least 3 characters long")
|
||||
.max(50, "IO name in Arabic must be at most 50 characters long"),
|
||||
|
||||
descriptionEnglish: yup
|
||||
.string()
|
||||
.required("Description in English is required")
|
||||
.min(10, "Description in English must be at least 10 characters long")
|
||||
.max(1000, "Description in English must be at most 1000 characters long"),
|
||||
|
||||
descriptionArabic: yup
|
||||
.string()
|
||||
.required("Description in Arabic is required")
|
||||
.min(10, "Description in Arabic must be at least 10 characters long")
|
||||
.max(2000, "Description in Arabic must be at most 500 characters long"),
|
||||
expectedReturnArabic: yup
|
||||
.string()
|
||||
.required("Expected return in Arabic is required"),
|
||||
|
||||
goalAmount: yup
|
||||
.number()
|
||||
.typeError("Goal Amount is must be number")
|
||||
.required('Goal amount is required')
|
||||
.positive('Goal amount must be a positive number')
|
||||
.min(IObyID?.data?.totalAmtInvestmentInUSD, `Goal amount should not be lesser then amount raised ${IObyID?.data?.totalAmtInvestmentInUSD}`),
|
||||
closingDate: yup
|
||||
.date()
|
||||
.notRequired("Closing date is required")
|
||||
.min(new Date(), "Closing date cannot be in the past"),
|
||||
|
||||
holdingPeriod: yup.string().required("Holding period is required"),
|
||||
holdingPeriodArabic: yup.string().required("Holding period is required"),
|
||||
isShariah: yup.string().required("CheckBox is required"),
|
||||
|
||||
|
||||
// minInvestmentAmount: yup
|
||||
// .number()
|
||||
// .required("Minimum investment is required")
|
||||
// .positive("Minimum investment must be a positive number")
|
||||
// .min(1, "Minimum investment must be at least 1"),
|
||||
|
||||
ISIN: yup.string().notRequired(),
|
||||
|
||||
InvestmentDetails: yup.string().notRequired(),
|
||||
|
||||
comment: yup.string().notRequired()
|
||||
.min(10, "Comment must be at least 10 characters long")
|
||||
.max(100, "Comment must be at most 100 characters long"),
|
||||
|
||||
expectedReturn: yup
|
||||
.string()
|
||||
.required("Expected return is required"),
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
const [values, setValues] = useState(id?minInvestmentById:miniValue);
|
||||
|
||||
|
||||
const formatNumber = (num) => {
|
||||
// Remove non-numeric characters and format with commas
|
||||
return num.replace(/\D/g, '')
|
||||
@@ -195,7 +252,7 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(schema),
|
||||
resolver: yupResolver(id ? schemaEdit : schema),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -220,17 +277,12 @@ 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 = [
|
||||
{
|
||||
@@ -313,20 +365,30 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
type: "text",
|
||||
isRequired: true,
|
||||
section: " ",
|
||||
width: "49%",
|
||||
width: "32.3%",
|
||||
value: IObyID?.data?.expectedReturn,
|
||||
},
|
||||
|
||||
{
|
||||
label: "Expected Return (Arabic)",
|
||||
name: "expectedReturnArabic",
|
||||
name: "expectedReturnArabic",
|
||||
type: "text",
|
||||
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,
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
@@ -371,6 +433,7 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
width: "32.3%",
|
||||
dateValue:formatDatee(IObyID?.data?.closingDate),
|
||||
// helperText: IObyID && `Current closing date is : ${formatDate(IObyID?.data?.closingDate)}`
|
||||
closingDate:true
|
||||
},
|
||||
{
|
||||
label: "ISIN",
|
||||
@@ -389,6 +452,8 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
section: " ",
|
||||
width: "32.3%",
|
||||
value: IObyID?.data?.InvestmentDetails,
|
||||
maxLength:20,
|
||||
helperText:`Maximum length should be 20 characters. You have entered ${watch()?.InvestmentDetails?.length || 0} characters.`
|
||||
},
|
||||
|
||||
{
|
||||
@@ -397,10 +462,10 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
name: "table",
|
||||
type: "table",
|
||||
section: " ",
|
||||
width: "100%",
|
||||
width: "100%",
|
||||
isRequired: true,
|
||||
options: investmentTypeOptions,
|
||||
handleInputChange: handleInputChange,
|
||||
handleInputChange:id ? handleInputChangeEdit : handleInputChangeCreate,
|
||||
value: values,
|
||||
},
|
||||
|
||||
@@ -417,151 +482,6 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
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]) {
|
||||
@@ -572,27 +492,30 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
}, {});
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
delete data.table;
|
||||
setIsLoading(true);
|
||||
// console.log(data);
|
||||
|
||||
const updatedMinAmount = values?.map(({id, value})=>{
|
||||
delete data.table;
|
||||
|
||||
setIsLoading(true);
|
||||
const updatedMinAmount = values?.map(({id, value, _id})=>{
|
||||
return {
|
||||
id:_id,
|
||||
country_xid:id,
|
||||
minInvestmentAmt: Number(value)
|
||||
}
|
||||
})
|
||||
|
||||
// console.log(formatDateToYYYYMMDD(data.closingDate));
|
||||
const formData = {
|
||||
...data,
|
||||
investmentType_xid: Number(data.investmentType),
|
||||
sponsor_xid: Number(data.sponserName),
|
||||
minInvestmentAmt:updatedMinAmount
|
||||
minInvestmentAmt:updatedMinAmount,
|
||||
closingDate: formatDateToYYYYMMDD(data.closingDate)
|
||||
};
|
||||
|
||||
// console.log(formData);
|
||||
// console.log(formData);
|
||||
if (id) {
|
||||
console.log("========================",formData);
|
||||
const res = await updateIO({ data: formData, id });
|
||||
console.log(res);
|
||||
if (res?.data?.statusCode === 200) {
|
||||
@@ -603,6 +526,11 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
navigate(`/view-io/${id}`);
|
||||
enableNextTab(index);
|
||||
} else if(res?.error?.status === 400){
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
|
||||
});
|
||||
} else if(res?.error?.status === 500){
|
||||
setIsLoading(false);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
|
||||
@@ -610,6 +538,7 @@ const IODetails = ({ enableNextTab, index, data }) => {
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
console.log("========================",formData);
|
||||
const res = await creatIO(formData);
|
||||
console.log(res?.error?.status);
|
||||
if (res?.data?.statusCode === 200) {
|
||||
@@ -667,7 +596,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;
|
||||
});
|
||||
|
||||
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
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
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;
|
||||
191
src/Pages/IO_Management/CreateIO/IONAVDetails/IONAVDetails.jsx
Normal file
191
src/Pages/IO_Management/CreateIO/IONAVDetails/IONAVDetails.jsx
Normal file
@@ -0,0 +1,191 @@
|
||||
// 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() && (
|
||||
<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
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
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;
|
||||
@@ -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;
|
||||
298
src/Pages/IO_Management/CreateIO/IONAVDetailsOld.jsx
Normal file
298
src/Pages/IO_Management/CreateIO/IONAVDetailsOld.jsx
Normal file
@@ -0,0 +1,298 @@
|
||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
|
||||
import {
|
||||
Box,
|
||||
HStack,
|
||||
Input,
|
||||
Text,
|
||||
Table,
|
||||
Tbody,
|
||||
Th,
|
||||
Tr,
|
||||
Avatar,
|
||||
useDisclosure,
|
||||
Button,
|
||||
Badge,
|
||||
} from "@chakra-ui/react";
|
||||
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
|
||||
import Pagination from "../../../Components/Pagination";
|
||||
import NormalTable from "../../../Components/DataTable/NormalTable";
|
||||
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
|
||||
import { formatDatee } from "../../../Components/FormField";
|
||||
import { AddIcon } from "@chakra-ui/icons";
|
||||
import AddIONav from "./AddIONav";
|
||||
import { formatDate } from "../../Master/Sponser/Sponsers";
|
||||
import { LuFileSpreadsheet } from "react-icons/lu";
|
||||
import { exportToExcel, exportToExcelNew } from "../../../Constants/Constants";
|
||||
|
||||
const IONAVDetails = () => {
|
||||
const { navDetails, setNavDetails, 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;
|
||||
})
|
||||
.reverse()
|
||||
// .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)} /} */}
|
||||
{formatDate(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}`} /} */}
|
||||
<Badge ms={1} colorScheme="green" me={1}>$</Badge>
|
||||
{`${parseFloat(item.transactionAmount || 0).toLocaleString()}`}
|
||||
</Text>
|
||||
),
|
||||
"Last NAV update": (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.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>
|
||||
<Text
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
maxWidth="200px" // Adjust width as needed
|
||||
display="block" // Ensure block display for proper truncation
|
||||
overflow="hidden"
|
||||
isTruncated
|
||||
textOverflow="ellipsis"
|
||||
>
|
||||
{item.comments}
|
||||
</Text>
|
||||
),
|
||||
"Update by ": (
|
||||
<Text
|
||||
justifyContent={"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}
|
||||
</Text>
|
||||
),
|
||||
"Update On": (
|
||||
<Text
|
||||
justifyContent={"center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{formatDate(item.updatedAt)}
|
||||
</Text>
|
||||
),
|
||||
}));
|
||||
|
||||
const customHeaders = [
|
||||
{ label: "ID", key: "id" },
|
||||
{ label: "Valuation Date", key: "transactionDate" },
|
||||
{ label: "NAV", key: "transactionAmount" },
|
||||
{ label: "Last NAV update", key: "previousNAVvalue" },
|
||||
|
||||
{ label: "Investment Closed", key: "initialNAVvalue" },
|
||||
{ label: "Comments", key: "comments" },
|
||||
|
||||
// { label: "Update by", key: "creator" },
|
||||
{ label: "Transaction Type", key: "transactionType" },
|
||||
// { label: "Comments", key: "comments" },
|
||||
// Add more headers as needed
|
||||
];
|
||||
|
||||
console.log(IODetails?.ioNAVHistory);
|
||||
const ioNavExport = IODetails?.ioNAVHistory?.map((item, index) => ({
|
||||
"ID": item?.id, // Keep as integer if it's already a number
|
||||
"Valuation Date": formatDate(item?.transactionDate), // Assuming this is a date, no conversion needed
|
||||
"NAV": parseFloat(item?.transactionAmount) || 0, // Convert to float
|
||||
"Last NAV update": parseFloat(item?.previousNAVvalue) || 0, // Convert to float
|
||||
"Investment Closed": parseFloat(item?.initialNAVvalue) || 0, // Convert to float
|
||||
"Comments": item?.comments, // Keep as string
|
||||
"Transaction Type": item?.transactionType, // Keep as string
|
||||
"Update by ": item?.creator?.firstName, // Keep as string
|
||||
// "Update On": formatDate(item?.updatedAt) // Assuming this is a date, no conversion needed
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedNav = navDetails.filter((sponsor) => sponsor.id !== actionId);
|
||||
|
||||
setTimeout(() => {
|
||||
setNavDetails(updatedNav);
|
||||
setDeleteAlert(false);
|
||||
setIsLoading(false);
|
||||
}, 100);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
const handleExport = () => {};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} pb={0}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
pb={3}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<HStack display={"flex"} alignItems={"center"}>
|
||||
<Button
|
||||
onClick={() =>
|
||||
exportToExcelNew(ioNavExport, "Io Nav details")
|
||||
}
|
||||
leftIcon={<LuFileSpreadsheet />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
variant={"outline"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
isDisabled={ioNavExport?.length === 0}
|
||||
>
|
||||
Export xls
|
||||
</Button>
|
||||
|
||||
{IODetails?.isInvestedAmount ? (
|
||||
<Button
|
||||
onClick={onOpen}
|
||||
leftIcon={<AddIcon />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
>
|
||||
Add IO Nav
|
||||
</Button>
|
||||
) : null}
|
||||
</HStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
centered={true}
|
||||
emptyMessage={`We don't have any Sponers`}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
caption={"Tanami v1.0"}
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
|
||||
<AddIONav isOpen={isOpen} onClose={onClose} firstField={firstField} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default IONAVDetails;
|
||||
@@ -0,0 +1,179 @@
|
||||
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 { useApproveDistributedMutation } 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 ApproveDistrubationModal = ({ isOpen, onClose, firstField ,id, onBigModalClose}) => {
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
const [ approveDistributed ] = useApproveDistributedMutation()
|
||||
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await approveDistributed({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()
|
||||
onBigModalClose()
|
||||
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={"xs"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"xs"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApproveDistrubationModal;
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import { useApproveInvestedMutation } 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 ApproveInvestedModal = ({ isOpen, onClose, firstField ,id,onBigModalClose}) => {
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
const [ approveInvested ] = useApproveInvestedMutation()
|
||||
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await approveInvested({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()
|
||||
onBigModalClose()
|
||||
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={"xs"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"xs"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApproveInvestedModal;
|
||||
|
||||
249
src/Pages/IO_Management/CreateIO/IOTransaction/Approved.jsx
Normal file
249
src/Pages/IO_Management/CreateIO/IOTransaction/Approved.jsx
Normal file
@@ -0,0 +1,249 @@
|
||||
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";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString();
|
||||
|
||||
const Approved = () => {
|
||||
const toast = useToast();
|
||||
const firstField = useRef();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { IODetails, iOTransaction, setIOTransaction } =
|
||||
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",
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Table filter
|
||||
const filteredData = IODetails?.ioTransactionRecords?.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.",
|
||||
"Transaction Date",
|
||||
"Transaction Name",
|
||||
"Amount",
|
||||
"Created By",
|
||||
"Created On",
|
||||
"Approved By",
|
||||
"Approved 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 Name": (
|
||||
<Text
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{item?.transactionType}
|
||||
</Text>
|
||||
),
|
||||
"Amount": (
|
||||
<Text
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{/* {item?.transactionAmount} */}
|
||||
{parseFloat(item?.transactionAmount || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Text>
|
||||
),
|
||||
"Created By": (
|
||||
<Text
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
textTransform={'capitalize'}
|
||||
>
|
||||
{item?.creator?.firstName}
|
||||
</Text>
|
||||
),
|
||||
"Created On": (
|
||||
<Text
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{formatDate(item?.createdAt)}
|
||||
</Text>
|
||||
),
|
||||
"Approved By": (
|
||||
<>
|
||||
<Text
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
textTransform={'capitalize'}
|
||||
>
|
||||
{item?.modifier?.firstName}
|
||||
</Text>
|
||||
{/* <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?.creator?.firstName}
|
||||
</Text> */}
|
||||
</>
|
||||
),
|
||||
"Approved On": (
|
||||
<Text
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{item?.modifier ? formatDate(item?.updatedAt) : "---" }
|
||||
{}
|
||||
</Text>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedSponsors = sponser.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setCaseDetails(updatedSponsors);
|
||||
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>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
emptyMessage={`We don't have any Sponers`}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
setMouseEnteredId={setMouseEnteredId}
|
||||
setMouseEntered={setMouseEntered}
|
||||
/>
|
||||
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message={"Are you sure you want to delete sponers?"}
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Approved;
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { useForm } from "react-hook-form";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import {useApproveCancleTransactionMutation, useApproveExitTransactionMutation} from "../../../../Services/io.service";
|
||||
|
||||
|
||||
const ApprovedCancelTransaction = ({ isOpen, onClose, firstField ,id,onBigModalClose}) => {
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(),
|
||||
});
|
||||
|
||||
const [ approveCancleTransaction ] = useApproveCancleTransactionMutation()
|
||||
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await approveCancleTransaction({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()
|
||||
onBigModalClose()
|
||||
setIsBtnLoading(false)
|
||||
}else{
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={'error'} message={"Something went wrong"} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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={"xs"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"xs"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApprovedCancelTransaction;
|
||||
|
||||
175
src/Pages/IO_Management/CreateIO/IOTransaction/ApprovedExit.jsx
Normal file
175
src/Pages/IO_Management/CreateIO/IOTransaction/ApprovedExit.jsx
Normal file
@@ -0,0 +1,175 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
Textarea,
|
||||
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 { useApproveExitTransactionMutation } 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 ApprovedExit = ({ isOpen, onClose, firstField ,id,onBigModalClose}) => {
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
const [ approveExitTransaction ] = useApproveExitTransactionMutation()
|
||||
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await approveExitTransaction({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()
|
||||
onBigModalClose()
|
||||
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={"xs"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"xs"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApprovedExit;
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import {
|
||||
Badge,
|
||||
Tab,
|
||||
TabList,
|
||||
TabPanel,
|
||||
TabPanels,
|
||||
Tabs,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext } from "react";
|
||||
import Approved from "./Approved";
|
||||
import Pending from "./Pending";
|
||||
import Rejected from "./Rejected";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
|
||||
const IOTransaction = () => {
|
||||
const { IODetails } = useContext(GlobalStateContext);
|
||||
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
|
||||
{IODetails?.ioTransactionRecords?.Pending.length > 0 && (
|
||||
<Badge rounded={"sm"} colorScheme="forestGreen" ms={2}>
|
||||
{IODetails?.ioTransactionRecords?.Pending.length || 0}
|
||||
</Badge>
|
||||
)}
|
||||
</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 IOTransaction;
|
||||
286
src/Pages/IO_Management/CreateIO/IOTransaction/Pending.jsx
Normal file
286
src/Pages/IO_Management/CreateIO/IOTransaction/Pending.jsx
Normal file
@@ -0,0 +1,286 @@
|
||||
import { ViewIcon } from "@chakra-ui/icons";
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Input,
|
||||
Text,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
|
||||
import NormalTable from "../../../../Components/DataTable/NormalTable";
|
||||
import { isMaker } from "../../../../Constants/Constants";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
|
||||
import RequestApproveModal from "./RequestApproveModal";
|
||||
import RequestRejectModal from "./RequestRejectModal";
|
||||
import ViewAmountInvested from "./ViewAmountInvested";
|
||||
import ViewCancel from "./ViewCancel";
|
||||
import ViewDistributionInvestor from "./ViewDistributionInvestor";
|
||||
import ViewExit from "./ViewExit";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString();
|
||||
|
||||
const Pending = () => {
|
||||
const toast = useToast();
|
||||
const firstField = useRef();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { IODetails, iOTransaction, setIOTransaction } =
|
||||
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 [distributedAmt, setDistributedAmt] = useState();
|
||||
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
onOpen: onConfirmOpen,
|
||||
onClose: onConfirmClose,
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isRejectOpen,
|
||||
onOpen: onRejectOpen,
|
||||
onClose: onRejectClose,
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isInvestmentOpen,
|
||||
onOpen: onInvestmentOpen,
|
||||
onClose: onInvestmentClose,
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isDistInvestorOpen,
|
||||
onOpen: onDistInvestorOpen,
|
||||
onClose: onDistInvestorClose,
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isExitOpen,
|
||||
onOpen: onExitOpen,
|
||||
onClose: onExitClose,
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isCancelOpen,
|
||||
onOpen: onCancelOpen,
|
||||
onClose: onCancelClose,
|
||||
} = 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?.ioTransactionRecords?.Pending?.filter((item) => {
|
||||
// // Filter by name (case insensitive)
|
||||
// const name = item.transactionName;
|
||||
// const searchLower = searchTerm?.toLowerCase();
|
||||
// const nameMatches = name?.toLowerCase().includes(searchLower);
|
||||
// return nameMatches;
|
||||
// });
|
||||
|
||||
const tableHeadRow = [
|
||||
"Sr No.",
|
||||
"Transaction Date",
|
||||
"Transaction Name",
|
||||
"Amount",
|
||||
"Created By",
|
||||
"Created On",
|
||||
"Approved By",
|
||||
"Approved On",
|
||||
"Actions",
|
||||
];
|
||||
|
||||
const extractedArray = IODetails?.ioTransactionRecords?.Pending?.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 Name": (
|
||||
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
|
||||
{item?.transactionType}
|
||||
</Text>
|
||||
),
|
||||
Amount: (
|
||||
<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>
|
||||
),
|
||||
"Created By": (
|
||||
<Text
|
||||
textTransform={"capitalize"}
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{item?.creator?.firstName}
|
||||
</Text>
|
||||
),
|
||||
"Created On": (
|
||||
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
{formatDate(item?.createdAt)}
|
||||
</Text>
|
||||
),
|
||||
"Approved By": (
|
||||
<Text
|
||||
textTransform={"capitalize"}
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{item?.modifier?.firstName}
|
||||
</Text>
|
||||
),
|
||||
"Approved On": (
|
||||
<Text w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}>
|
||||
{item?.modifier ? formatDate(item?.updatedAt) : "---"}
|
||||
</Text>
|
||||
),
|
||||
Actions: (
|
||||
<Box display={"flex"} justifyContent={"center"} gap={2}>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
px={2}
|
||||
py={1}
|
||||
fontWeight={500}
|
||||
onClick={() => {
|
||||
setActionId(item.id); // Set the action ID for all cases
|
||||
if (item?.transactionType === "Amount Invested") {
|
||||
onInvestmentOpen();
|
||||
} else if (item?.transactionType === "Distribution To Investor") {
|
||||
onDistInvestorOpen();
|
||||
setDistributedAmt(item?.transactionAmount);
|
||||
} else if (item?.transactionType === "Exit") {
|
||||
onExitOpen();
|
||||
} else if (item?.transactionType === "Cancel") {
|
||||
onCancelOpen();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{isMaker() ? <ViewIcon me={"4px"} /> : null}{" "}
|
||||
{isMaker() ? "View" : "Approve / Reject"}
|
||||
</Button>
|
||||
</Box>
|
||||
),
|
||||
})
|
||||
);
|
||||
|
||||
const handleDelete = () => {
|
||||
const updatedSponsors = sponser.filter(
|
||||
(sponsor) => sponsor.id !== actionId
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
setCaseDetails(updatedSponsors);
|
||||
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>
|
||||
</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}
|
||||
/>
|
||||
|
||||
<ViewAmountInvested
|
||||
isOpen={isInvestmentOpen}
|
||||
onClose={onInvestmentClose}
|
||||
id={actionId}
|
||||
/>
|
||||
<ViewDistributionInvestor
|
||||
isOpen={isDistInvestorOpen}
|
||||
onClose={onDistInvestorClose}
|
||||
id={actionId}
|
||||
amount={distributedAmt}
|
||||
/>
|
||||
<ViewExit isOpen={isExitOpen} onClose={onExitClose} id={actionId} />
|
||||
<ViewCancel isOpen={isCancelOpen} onClose={onCancelClose} id={actionId} />
|
||||
<RequestApproveModal
|
||||
// data={data?.data?.rows}
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
id={actionId}
|
||||
// firstField={firstField}
|
||||
/>
|
||||
<RequestRejectModal
|
||||
isOpen={isRejectOpen}
|
||||
onClose={onRejectClose}
|
||||
id={actionId}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Pending;
|
||||
219
src/Pages/IO_Management/CreateIO/IOTransaction/Rejected.jsx
Normal file
219
src/Pages/IO_Management/CreateIO/IOTransaction/Rejected.jsx
Normal file
@@ -0,0 +1,219 @@
|
||||
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";
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString();
|
||||
|
||||
const Rejected = () => {
|
||||
const toast = useToast();
|
||||
const firstField = useRef();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { IODetails, iOTransaction, setIOTransaction } =
|
||||
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",
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Table filter
|
||||
// const filteredData = IODetails?.ioTransactionRecords?.Reject?.filter((item) => {
|
||||
// // Filter by name (case insensitive)
|
||||
// const name = item.transactionName;
|
||||
// const searchLower = searchTerm.toLowerCase();
|
||||
// const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
// return nameMatches;
|
||||
// });
|
||||
|
||||
const tableHeadRow = [
|
||||
"Sr No.",
|
||||
"Transaction Name",
|
||||
"Amount",
|
||||
"Created By",
|
||||
"Created On",
|
||||
"Approved By",
|
||||
"Approved On",
|
||||
];
|
||||
|
||||
const extractedArray = IODetails?.ioTransactionRecords?.Reject?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr No.": (
|
||||
<Text
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{index + 1}.
|
||||
</Text>
|
||||
),
|
||||
"Transaction Name": (
|
||||
<Text
|
||||
as={"span"}
|
||||
color={"gray.600"}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{item?.transactionType}
|
||||
</Text>
|
||||
),
|
||||
"Amount": (
|
||||
<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>
|
||||
),
|
||||
"Created By": (
|
||||
<Text
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
textTransform={'capitalize'}
|
||||
>
|
||||
{item?.creator?.firstName}
|
||||
</Text>
|
||||
),
|
||||
"Created On": (
|
||||
<Text
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
>
|
||||
{formatDate(item?.createdAt)}
|
||||
</Text>
|
||||
),
|
||||
"Approved By": (
|
||||
<Text
|
||||
w={"100px"}
|
||||
as={"span"}
|
||||
color={"gray.800"}
|
||||
fontWeight={"500"}
|
||||
textTransform={'capitalize'}
|
||||
>
|
||||
{item?.modifier?.firstName}
|
||||
</Text>
|
||||
),
|
||||
"Approved 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);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} pb={0}>
|
||||
<Box bg="white.500">
|
||||
<HStack
|
||||
display={"flex"}
|
||||
justifyContent={"space-between"}
|
||||
pb={3}
|
||||
spacing="24px"
|
||||
>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
<NormalTable
|
||||
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 Rejected;
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
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 { useApproveDistributionMutation, 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 [ approveDistribution ] = useApproveDistributionMutation()
|
||||
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await approveDistribution({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,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 { useApproveDistributionMutation, useApproveExitMutation, 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 [ approveExit ] = useApproveExitMutation()
|
||||
|
||||
|
||||
const onSubmit = async(data) => {
|
||||
setIsBtnLoading(true)
|
||||
try {
|
||||
const res = await approveExit({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,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 { 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, onBigModalClose}) => {
|
||||
const [isBtnLoading , setIsBtnLoading] = useState(false)
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
const {
|
||||
register,
|
||||
reset,
|
||||
watch,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(conformModalSchema),
|
||||
});
|
||||
|
||||
const [ rejectIOCase ] = useRejectIOCaseMutation()
|
||||
|
||||
|
||||
const handleFileChange = (event) => {
|
||||
const selectedFile = event.target.files[0];
|
||||
setFile(selectedFile);
|
||||
};
|
||||
|
||||
|
||||
const { data, isLoading } =
|
||||
(id, {
|
||||
skip: !id,
|
||||
});
|
||||
|
||||
console.log("============data",data);
|
||||
|
||||
|
||||
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()
|
||||
onBigModalClose()
|
||||
setIsBtnLoading(false)
|
||||
}else{
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox status={'error'} message={"Something went wrong"} />
|
||||
),
|
||||
});
|
||||
setIsBtnLoading(false)
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// useEffect(() => {
|
||||
// if (data) {
|
||||
// reset({
|
||||
// investorAmount: data?.data?.investorAmount,
|
||||
// });
|
||||
// }
|
||||
// }, [data, reset]);
|
||||
|
||||
const heandleOnClose = () =>{
|
||||
reset()
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isCentered isOpen={isOpen} onClose={heandleOnClose} initialFocusRef={firstField}>
|
||||
<ModalOverlay />
|
||||
<ModalContent pb={4}>
|
||||
<ModalHeader fontSize={"md"}>Reject Comment</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
{isLoading ? (
|
||||
<FullscreenLoaders height={"50vh"} />
|
||||
) : (
|
||||
<Box as="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<ModalBody>
|
||||
<FormControl mb={4} isRequired>
|
||||
<FormLabel fontSize="sm">Comment</FormLabel>
|
||||
<Textarea
|
||||
rows={6}
|
||||
focusBorderColor="green.400"
|
||||
name="comments"
|
||||
{...register("comments")}
|
||||
fontSize="sm"
|
||||
type="textarea"
|
||||
size="md"
|
||||
placeholder={"Enter your comments...."}
|
||||
rounded={"md"}
|
||||
resize={"none"}
|
||||
/>
|
||||
{errors.comments && (
|
||||
<Text fontSize="xs" color="red">
|
||||
{errors.comments.message}
|
||||
</Text>
|
||||
)}
|
||||
<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={"xs"}
|
||||
rounded={"sm"}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="forestGreen"
|
||||
variant="solid"
|
||||
size={"xs"}
|
||||
rounded={"sm"}
|
||||
isLoading={isBtnLoading}
|
||||
type="submit"
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Box>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RequestRejectModal;
|
||||
@@ -0,0 +1,295 @@
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useAmountIvestmentMutation } from "../../../../Services/io.service";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import CurrencyInput from "../../../../Components/CurrencyInput";
|
||||
import RequestRejectModal from "./RequestRejectModal";
|
||||
import ApproveInvestedModal from "./ApproveInvestedModal";
|
||||
import { formatDate } from "../../../Master/Sponser/Sponsers";
|
||||
import { encryptString, isMaker } from "../../../../Constants/Constants";
|
||||
|
||||
// Validation schema
|
||||
const validationSchema = yup.object().shape({
|
||||
transactionDate: yup.date().required("Date is required"),
|
||||
Total_Amount: yup.number().required("Amount is required"),
|
||||
amountInvested: yup.number().required("Amount to invest is required"),
|
||||
IoCash: yup
|
||||
.number()
|
||||
.positive("IO Cash must be positive")
|
||||
.required("IO Cash is required"),
|
||||
});
|
||||
|
||||
// Function to format currency
|
||||
const formatCurrency = (value) => {
|
||||
if (isNaN(value)) return "";
|
||||
const formatted = parseFloat(value).toFixed(2).toString();
|
||||
const [integer, decimal] = formatted.split(".");
|
||||
const formattedInteger = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
return decimal ? `${formattedInteger}.${decimal}` : formattedInteger;
|
||||
};
|
||||
|
||||
const ViewAmountInvested = ({ isOpen, onClose, id: investorId }) => {
|
||||
const params = useParams();
|
||||
const toast = useToast();
|
||||
const id = params?.id;
|
||||
const {
|
||||
control,
|
||||
register,
|
||||
handleSubmit,
|
||||
reset,
|
||||
watch,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: yupResolver(validationSchema),
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { IODetails } = useContext(GlobalStateContext);
|
||||
const [amountInvested] = useAmountIvestmentMutation();
|
||||
const [actionId, setActionId] = useState(false);
|
||||
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
onOpen: onConfirmOpen,
|
||||
onClose: onConfirmClose,
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isRejectOpen,
|
||||
onOpen: onRejectOpen,
|
||||
onClose: onRejectClose,
|
||||
} = useDisclosure();
|
||||
|
||||
useEffect(() => {
|
||||
if (IODetails?.totalAmtInvestmentInUSD) {
|
||||
const totalAmount = parseFloat(IODetails.totalAmtInvestmentInUSD);
|
||||
const ioCashUpdate = parseFloat(IODetails.totalAmtInvestmentInUSD);
|
||||
reset({
|
||||
Total_Amount: totalAmount,
|
||||
IoCash: ioCashUpdate,
|
||||
});
|
||||
}
|
||||
}, [IODetails, reset]);
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
console.log(data);
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const res = await amountInvested({ data, id });
|
||||
console.log(res);
|
||||
if (res?.data?.statusCode === 200) {
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.data?.message} />,
|
||||
});
|
||||
setIsLoading(false);
|
||||
onClose();
|
||||
} else if (res?.error?.status === 400) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.error?.data?.message} status={"error"} />
|
||||
),
|
||||
});
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// const formatDate = (date) => new Date(date).toLocaleDateString();
|
||||
|
||||
const handleAmountChange = (e) => {
|
||||
// e might be an object or just a value, handle both cases
|
||||
const amount =
|
||||
typeof e === "object" && e.target
|
||||
? parseFloat(e.target.value) || 0
|
||||
: parseFloat(e) || 0;
|
||||
const totalAmount = parseFloat(IODetails?.totalAmtInvestmentInUSD) || 0;
|
||||
const ioCash = (totalAmount - amount).toFixed(2);
|
||||
|
||||
reset({
|
||||
amountInvested: parseFloat(amount),
|
||||
IoCash: parseFloat(ioCash),
|
||||
Total_Amount: IODetails?.totalAmtInvestmentInUSD,
|
||||
});
|
||||
};
|
||||
|
||||
console.log(
|
||||
"=========hitttt",
|
||||
IODetails?.ioTransactionRecords?.Pending?.[0]?.createdAt
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader fontSize={"md"}>Amount Invested</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<FormControl
|
||||
mb={"15px"}
|
||||
isInvalid={!!errors.transactionDate}
|
||||
isRequired
|
||||
>
|
||||
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
|
||||
Date
|
||||
</FormLabel>
|
||||
<Input
|
||||
type="text"
|
||||
value={formatDate(
|
||||
IODetails?.ioTransactionRecords?.Pending?.[0]?.createdAt
|
||||
)}
|
||||
// value={IODetails?.ioTransactionRecords?.Pending?.[0]?.createdAt}
|
||||
size="sm"
|
||||
rounded={"sm"}
|
||||
textAlign={"end"}
|
||||
focusBorderColor="forestGreen.300"
|
||||
fontSize={"sm"}
|
||||
readOnly
|
||||
/>
|
||||
{/* <Text>
|
||||
{IODetails?.ioTransactionRecords?.Approved?.[0]?.createdAt} dccd
|
||||
</Text> */}
|
||||
</FormControl>
|
||||
|
||||
<FormControl
|
||||
mb={"15px"}
|
||||
isInvalid={!!errors.Total_Amount}
|
||||
isReadOnly
|
||||
>
|
||||
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
|
||||
Amount
|
||||
</FormLabel>
|
||||
<Input
|
||||
type="text"
|
||||
value={formatCurrency(watch("Total_Amount") || 0)}
|
||||
size="sm"
|
||||
rounded={"sm"}
|
||||
textAlign={"end"}
|
||||
focusBorderColor="forestGreen.300"
|
||||
fontSize={"sm"}
|
||||
readOnly
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl
|
||||
mb={"15px"}
|
||||
isInvalid={!!errors.amountInvested}
|
||||
isRequired
|
||||
>
|
||||
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
|
||||
Amount to invest
|
||||
</FormLabel>
|
||||
<Input
|
||||
type="text"
|
||||
value={formatCurrency(
|
||||
IODetails?.ioTransactionRecords?.Pending?.[0]
|
||||
?.transactionAmount || 0
|
||||
)}
|
||||
size="sm"
|
||||
rounded={"sm"}
|
||||
textAlign={"end"}
|
||||
focusBorderColor="forestGreen.300"
|
||||
fontSize={"sm"}
|
||||
readOnly
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl mb={"15px"} isInvalid={!!errors.IoCash}>
|
||||
<FormLabel as={"label"} fontSize={"sm"} fontWeight={500}>
|
||||
IO Cash
|
||||
</FormLabel>
|
||||
<Input
|
||||
type="text"
|
||||
value={formatCurrency(
|
||||
(watch("Total_Amount") || 0) -
|
||||
(IODetails?.ioTransactionRecords?.Pending?.[0]
|
||||
?.transactionAmount || 0)
|
||||
)}
|
||||
size="sm"
|
||||
rounded={"sm"}
|
||||
focusBorderColor="forestGreen.300"
|
||||
fontSize={"sm"}
|
||||
textAlign={"right"}
|
||||
readOnly
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
{!isMaker() && (
|
||||
<ModalFooter>
|
||||
<Box display={"flex"} justifyContent={"center"} gap={2}>
|
||||
<Button
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={3}
|
||||
py={2}
|
||||
onClick={() => {
|
||||
setActionId(id); // Use the `id` variable from params
|
||||
onConfirmOpen();
|
||||
}}
|
||||
colorScheme="forestGreen"
|
||||
variant={"solid"}
|
||||
cursor={"pointer"}
|
||||
>
|
||||
Approve
|
||||
</Button>
|
||||
<Button
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={3}
|
||||
py={2}
|
||||
onClick={() => {
|
||||
setActionId(id); // Use the `id` variable from params
|
||||
onRejectOpen();
|
||||
}}
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
</Box>
|
||||
</ModalFooter>
|
||||
)}
|
||||
</form>
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
<ApproveInvestedModal
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
onBigModalClose={onClose}
|
||||
id={investorId}
|
||||
/>
|
||||
<RequestRejectModal
|
||||
isOpen={isRejectOpen}
|
||||
onClose={onRejectClose}
|
||||
onBigModalClose={onClose}
|
||||
id={investorId}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewAmountInvested;
|
||||
369
src/Pages/IO_Management/CreateIO/IOTransaction/ViewCancel.jsx
Normal file
369
src/Pages/IO_Management/CreateIO/IOTransaction/ViewCancel.jsx
Normal file
@@ -0,0 +1,369 @@
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
HStack,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Table,
|
||||
Tbody,
|
||||
Text,
|
||||
Th,
|
||||
Tr,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import NormalData from "../../../../Components/DataTable/NormalTable";
|
||||
import { useContext, useState } from "react";
|
||||
import {
|
||||
useExitIOTransactionMutation,
|
||||
useGetDistributedToInvestorMutation,
|
||||
useGetDistributionInvestorMutation,
|
||||
useGetIOByIdQuery,
|
||||
} from "../../../../Services/io.service";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useEffect } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import ApprovedCancelTransaction from "./ApprovedCancelTransaction";
|
||||
import RequestRejectModal from "./RequestRejectModal";
|
||||
import { encryptString, isMaker } from "../../../../Constants/Constants";
|
||||
|
||||
const ViewCancel = ({ isOpen, onClose, id: cancleId }) => {
|
||||
const params = useParams();
|
||||
const toast = useToast();
|
||||
const id = params?.id;
|
||||
const [isCalculateLoading, setIsCalculateLoading] = useState(false);
|
||||
const [isFinalCalculateLoading, setIsFinalCalculateLoading] = useState(false);
|
||||
const [calcualtedData, setCalculatedDate] = useState(null);
|
||||
const [isCalcualtedData, setIsCalcualtedData] = useState(false);
|
||||
const { investors, setInvestors, slideFromRight, IODetails } =
|
||||
useContext(GlobalStateContext);
|
||||
|
||||
const [actionId, setActionId] = useState(false);
|
||||
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
onOpen: onConfirmOpen,
|
||||
onClose: onConfirmClose,
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isRejectOpen,
|
||||
onOpen: onRejectOpen,
|
||||
onClose: onRejectClose,
|
||||
} = useDisclosure();
|
||||
|
||||
const investorExit = yup.object().shape({
|
||||
amount: yup
|
||||
.string()
|
||||
.required("Amount is required")
|
||||
.test(
|
||||
"max",
|
||||
`Distribution amount should not be greater than IO cash amount ${IODetails?.ioCash}`,
|
||||
function (value) {
|
||||
const { ioCash } = IODetails || {}; // Safely get ioCash
|
||||
if (value && ioCash) {
|
||||
return parseFloat(value) <= parseFloat(ioCash); // Ensure both are compared as numbers
|
||||
}
|
||||
return true; // If ioCash is not available, skip validation
|
||||
}
|
||||
),
|
||||
});
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
reset,
|
||||
} = useForm({
|
||||
resolver: yupResolver(investorExit),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
console.log("hiit useEffectc");
|
||||
if (id && IODetails) {
|
||||
handleCalculate(id, {
|
||||
amount: IODetails?.ioMVNAV,
|
||||
});
|
||||
}
|
||||
reset({
|
||||
amount: IODetails?.ioMVNAV,
|
||||
});
|
||||
}, [IODetails, id]);
|
||||
|
||||
const handleCalculate = async (id, data) => {
|
||||
try {
|
||||
const res = await getDistributionInvestment({ id, data });
|
||||
console.log(res?.data?.data);
|
||||
|
||||
if (res?.error?.status === 401) {
|
||||
setIsCalculateLoading(false);
|
||||
setIsCalcualtedData(false);
|
||||
} else if (res?.data?.statusCode === 200) {
|
||||
setCalculatedDate(res?.data?.data);
|
||||
setIsCalculateLoading(false);
|
||||
setIsCalcualtedData(true);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const [getDistributionInvestment] = useGetDistributionInvestorMutation();
|
||||
|
||||
const investor = yup.object().shape({
|
||||
amount: yup.string().required("Amount is required"),
|
||||
});
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Client ID",
|
||||
"First name",
|
||||
"Last name",
|
||||
"Investment amount",
|
||||
"Percentage",
|
||||
"Market Value",
|
||||
"Return on Investment",
|
||||
"Distribution",
|
||||
"Distribution Percent",
|
||||
"Total Return",
|
||||
"Total return on Investment",
|
||||
];
|
||||
|
||||
const extractedArray = IODetails?.investors?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Client ID": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item?.clientReference_id}
|
||||
</Text>
|
||||
),
|
||||
"First name": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.firstName}
|
||||
</Text>
|
||||
),
|
||||
"Last name": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.lastName}
|
||||
</Text>
|
||||
),
|
||||
"Investment amount": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{/* {`$${formatCurrency(item.InvestedAmount_USD)}`} */}
|
||||
{`${parseFloat(item.InvestedAmount_USD || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}`}
|
||||
</Text>
|
||||
),
|
||||
Percentage: (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.Investor_Holidings} %
|
||||
</Text>
|
||||
),
|
||||
"Market Value": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{`${parseFloat(item.Market_Value || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}`}
|
||||
</Text>
|
||||
),
|
||||
"Return on Investment": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
h={6}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.Return_On_Investment || 0} %
|
||||
</Text>
|
||||
),
|
||||
Distribution: (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{/* {`$${item.Distribution_Amt}`} */}
|
||||
{`${parseFloat(item.Distribution_Amt || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}`}
|
||||
</Text>
|
||||
),
|
||||
"Distribution Percent": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{/* {`$${item.Distribution_Amt}`} */}
|
||||
{`${parseFloat(item.Distribution_Per || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})} %`}
|
||||
</Text>
|
||||
),
|
||||
"Total Return": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{/* {`$${formatCurrency(item.Total_Return) || 0}`} */}
|
||||
{`${parseFloat(item.Total_Return || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}`}
|
||||
</Text>
|
||||
),
|
||||
"Total return on Investment": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.Total_Return_On_Investment || 0} %
|
||||
</Text>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
setIsFinalCalculateLoading(false);
|
||||
setIsCalcualtedData(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal size={"xl"} isOpen={isOpen} onClose={handleClose}>
|
||||
<ModalOverlay />
|
||||
<ModalContent maxW={1000}>
|
||||
<ModalHeader fontSize={"md"}>Cancel Transaction</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<NormalData
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
/>
|
||||
</ModalBody>
|
||||
{!isMaker() && (
|
||||
<ModalFooter pt={0}>
|
||||
<Box display={"flex"} justifyContent={"center"} gap={2}>
|
||||
<Button
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={3}
|
||||
py={2}
|
||||
onClick={() => {
|
||||
setActionId(id); // Use the `id` variable from params
|
||||
onConfirmOpen();
|
||||
}}
|
||||
colorScheme="forestGreen"
|
||||
variant={"solid"}
|
||||
cursor={"pointer"}
|
||||
>
|
||||
Approve
|
||||
</Button>
|
||||
<Button
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={3}
|
||||
py={2}
|
||||
onClick={() => {
|
||||
setActionId(id); // Use the `id` variable from params
|
||||
onRejectOpen();
|
||||
}}
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
</Box>
|
||||
</ModalFooter>
|
||||
)}
|
||||
</ModalContent>
|
||||
<ApprovedCancelTransaction
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
onBigModalClose={onClose}
|
||||
id={cancleId}
|
||||
/>
|
||||
<RequestRejectModal
|
||||
isOpen={isRejectOpen}
|
||||
onClose={onRejectClose}
|
||||
onBigModalClose={onClose}
|
||||
id={cancleId}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewCancel;
|
||||
@@ -0,0 +1,271 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import NormalData from "../../../../Components/DataTable/NormalTable";
|
||||
import { useContext, useState } from "react";
|
||||
import { useGetDistributionInvestorMutation } from "../../../../Services/io.service";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useEffect } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import ApproveDistrubationModal from "./ApproveDistrubationModal";
|
||||
import RequestRejectModal from "./RequestRejectModal";
|
||||
import { encryptString, isMaker } from "../../../../Constants/Constants";
|
||||
|
||||
const ViewDistributionInvestor = ({ isOpen, onClose, id: exitId, amount }) => {
|
||||
const params = useParams();
|
||||
const toast = useToast();
|
||||
const id = params?.id;
|
||||
const [isCalculateLoading, setIsCalculateLoading] = useState(false);
|
||||
const [isFinalCalculateLoading, setIsFinalCalculateLoading] = useState(false);
|
||||
const [calcualtedData, setCalculatedDate] = useState(null);
|
||||
const [isCalcualtedData, setIsCalcualtedData] = useState(false);
|
||||
const { IODetails } = useContext(GlobalStateContext);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
|
||||
const investorExit = yup.object().shape({
|
||||
amount: yup
|
||||
.string()
|
||||
.required("Amount is required")
|
||||
.test(
|
||||
"max",
|
||||
`Distribution amount should not be greater than IO cash amount ${IODetails?.ioCash}`,
|
||||
function (value) {
|
||||
const { ioCash } = IODetails || {}; // Safely get ioCash
|
||||
if (value && ioCash) {
|
||||
return parseFloat(value) <= parseFloat(ioCash); // Ensure both are compared as numbers
|
||||
}
|
||||
return true; // If ioCash is not available, skip validation
|
||||
}
|
||||
),
|
||||
});
|
||||
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
onOpen: onConfirmOpen,
|
||||
onClose: onConfirmClose,
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isRejectOpen,
|
||||
onOpen: onRejectOpen,
|
||||
onClose: onRejectClose,
|
||||
} = useDisclosure();
|
||||
|
||||
const {
|
||||
formState: { errors },
|
||||
reset,
|
||||
} = useForm({
|
||||
resolver: yupResolver(investorExit),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (id && IODetails) {
|
||||
handleCalculate(id, {
|
||||
amount: Math.abs(amount),
|
||||
});
|
||||
}
|
||||
reset({
|
||||
amount: amount,
|
||||
});
|
||||
}, [IODetails, id, amount]);
|
||||
|
||||
const handleCalculate = async (id, data) => {
|
||||
try {
|
||||
const res = await getDistributionInvestment({ id, data });
|
||||
console.log(res?.data?.data);
|
||||
|
||||
if (res?.error?.status === 401) {
|
||||
setIsCalculateLoading(false);
|
||||
setIsCalcualtedData(false);
|
||||
} else if (res?.data?.statusCode === 200) {
|
||||
setCalculatedDate(res?.data?.data);
|
||||
setIsCalculateLoading(false);
|
||||
setIsCalcualtedData(false);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const [getDistributionInvestment] = useGetDistributionInvestorMutation();
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr No.",
|
||||
"Client Id",
|
||||
"First name",
|
||||
"Last Name",
|
||||
"Amount",
|
||||
"Holding (%)",
|
||||
"Distriution Amt($)",
|
||||
"Yeild (%)",
|
||||
];
|
||||
|
||||
const extractedArray = calcualtedData?.data?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr No.": (
|
||||
<Box
|
||||
w={9}
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
isTruncated={true}
|
||||
h={25}
|
||||
>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{index + 1}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Client Id": (
|
||||
<Box w={90} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.clientId}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"First name": (
|
||||
<Box w={90} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box minW={24} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
Amount: (
|
||||
<Box minW={24} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.amount?.toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Holding (%)": (
|
||||
<Box minW={24} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.investor_holidings?.toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
%
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Distriution Amt($)": (
|
||||
<Box minW={24} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.distribution_amt?.toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Yeild (%)": (
|
||||
<Box minW={24} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.distribution_per?.toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
%
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
setIsFinalCalculateLoading(false);
|
||||
setIsCalcualtedData(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal size={"xl"} isOpen={isOpen} onClose={handleClose}>
|
||||
<ModalOverlay />
|
||||
<ModalContent maxW={1000}>
|
||||
<ModalHeader fontSize={"md"}>
|
||||
Distribution To Investor Transaction
|
||||
</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<NormalData
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
/>
|
||||
</ModalBody>
|
||||
{/* ...(localStorage?.getItem("role") !== "Maker" ? ["Status"] : []), */}
|
||||
{!isMaker() && (
|
||||
<ModalFooter pt={0}>
|
||||
<Box display={"flex"} justifyContent={"center"} gap={2}>
|
||||
<Button
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={3}
|
||||
py={2}
|
||||
onClick={() => {
|
||||
setActionId(id); // Use the `id` variable from params
|
||||
onConfirmOpen();
|
||||
}}
|
||||
colorScheme="forestGreen"
|
||||
variant={"solid"}
|
||||
cursor={"pointer"}
|
||||
>
|
||||
Approve
|
||||
</Button>
|
||||
<Button
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={3}
|
||||
py={2}
|
||||
onClick={() => {
|
||||
setActionId(id); // Use the `id` variable from params
|
||||
onRejectOpen();
|
||||
}}
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
</Box>
|
||||
</ModalFooter>
|
||||
)}
|
||||
</ModalContent>
|
||||
<ApproveDistrubationModal
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
onBigModalClose={handleClose}
|
||||
id={exitId}
|
||||
/>
|
||||
<RequestRejectModal
|
||||
isOpen={isRejectOpen}
|
||||
onClose={onRejectClose}
|
||||
onBigModalClose={handleClose}
|
||||
id={exitId}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewDistributionInvestor;
|
||||
332
src/Pages/IO_Management/CreateIO/IOTransaction/ViewExit.jsx
Normal file
332
src/Pages/IO_Management/CreateIO/IOTransaction/ViewExit.jsx
Normal file
@@ -0,0 +1,332 @@
|
||||
import {
|
||||
Alert,
|
||||
AlertIcon,
|
||||
Box,
|
||||
Button,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormLabel,
|
||||
HStack,
|
||||
Input,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalOverlay,
|
||||
Switch,
|
||||
Table,
|
||||
Tbody,
|
||||
Text,
|
||||
Textarea,
|
||||
Th,
|
||||
Tr,
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import NormalData from "../../../../Components/DataTable/NormalTable";
|
||||
import { useContext, useState } from "react";
|
||||
import { AddIcon } from "@chakra-ui/icons";
|
||||
import {
|
||||
useExitIOTransactionMutation,
|
||||
useGetDistributedToInvestorMutation,
|
||||
useGetDistributionInvestorMutation,
|
||||
useUpdateExitToInvestorMutation,
|
||||
} from "../../../../Services/io.service";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import ToastBox from "../../../../Components/ToastBox";
|
||||
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
|
||||
import ApprovedExit from "./ApprovedExit";
|
||||
import RequestRejectModal from "./RequestRejectModal";
|
||||
import { encryptString, isMaker } from "../../../../Constants/Constants";
|
||||
|
||||
const ViewExit = ({ isOpen, onClose, id: investerId }) => {
|
||||
const params = useParams();
|
||||
const toast = useToast();
|
||||
const id = params?.id;
|
||||
const [isCalculateLoading, setIsCalculateLoading] = useState(false);
|
||||
const [isFinalCalculateLoading, setIsFinalCalculateLoading] = useState(false);
|
||||
const [calcualtedData, setCalculatedDate] = useState(null);
|
||||
const [isCalcualtedData, setIsCalcualtedData] = useState(false);
|
||||
const { IODetails } = useContext(GlobalStateContext);
|
||||
const [actionId, setActionId] = useState(false);
|
||||
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
onOpen: onConfirmOpen,
|
||||
onClose: onConfirmClose,
|
||||
} = useDisclosure();
|
||||
const {
|
||||
isOpen: isRejectOpen,
|
||||
onOpen: onRejectOpen,
|
||||
onClose: onRejectClose,
|
||||
} = useDisclosure();
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
reset,
|
||||
} = useForm({
|
||||
resolver: yupResolver(),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
console.log("hiit useEffectc");
|
||||
if (id && IODetails) {
|
||||
handleCalculate(id, {
|
||||
amount: IODetails?.ioMVNAV,
|
||||
});
|
||||
}
|
||||
reset({
|
||||
amount: IODetails?.ioMVNAV,
|
||||
});
|
||||
}, [IODetails, id]);
|
||||
|
||||
const handleCalculate = async (id, data) => {
|
||||
try {
|
||||
const res = await getDistributionInvestment({ id, data });
|
||||
console.log(res?.data?.data);
|
||||
|
||||
if (res?.error?.status === 401) {
|
||||
setIsCalculateLoading(false);
|
||||
setIsCalcualtedData(false);
|
||||
} else if (res?.data?.statusCode === 200) {
|
||||
setCalculatedDate(res?.data?.data);
|
||||
setIsCalculateLoading(false);
|
||||
setIsCalcualtedData(true);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const [getDistributionInvestment] = useGetDistributionInvestorMutation();
|
||||
|
||||
const investor = yup.object().shape({
|
||||
amount: yup.string().required("Amount is required"),
|
||||
});
|
||||
|
||||
// ====================================================[Table Setup]================================================================
|
||||
const tableHeadRow = [
|
||||
"Sr No.",
|
||||
"Client Id",
|
||||
"First name",
|
||||
"Last Name",
|
||||
"Amount",
|
||||
"Holding (%)",
|
||||
"Exit Amt($)",
|
||||
];
|
||||
|
||||
const extractedArray = calcualtedData?.data?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
"Sr No.": (
|
||||
<Box
|
||||
w={9}
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
isTruncated={true}
|
||||
h={25}
|
||||
>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{index + 1}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Client Id": (
|
||||
<Box w={100} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.clientId}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"First name": (
|
||||
<Box minW={24} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.firstName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Last Name": (
|
||||
<Box minW={24} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.lastName}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
Amount: (
|
||||
<Box minW={24} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.amount?.toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Holding (%)": (
|
||||
<Box minW={24} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.investor_holidings?.toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
%
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
"Exit Amt($)": (
|
||||
<Box minW={24} isTruncated={true}>
|
||||
<Text as={"span"} color={"teal.900"} fontWeight={"500"}>
|
||||
{item?.distribution_amt?.toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Text>
|
||||
</Box>
|
||||
),
|
||||
}));
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
setIsCalculateLoading(true);
|
||||
|
||||
try {
|
||||
const res = await getDistributionInvestment({ id, data });
|
||||
console.log(res?.data?.data);
|
||||
|
||||
if (res?.error?.status === 401) {
|
||||
toast({
|
||||
render: () => (
|
||||
<ToastBox message={res?.error?.data?.message} status={"error"} />
|
||||
),
|
||||
});
|
||||
setIsCalculateLoading(false);
|
||||
setIsCalcualtedData(false);
|
||||
} else if (res?.data?.statusCode === 200) {
|
||||
setCalculatedDate(res?.data?.data);
|
||||
toast({
|
||||
render: () => <ToastBox message={res?.data?.message} />,
|
||||
});
|
||||
setIsCalculateLoading(false);
|
||||
setIsCalcualtedData(true);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
setIsFinalCalculateLoading(false);
|
||||
setIsCalcualtedData(false);
|
||||
};
|
||||
|
||||
console.log(id);
|
||||
|
||||
return (
|
||||
<Modal size={"xl"} isOpen={isOpen} onClose={handleClose}>
|
||||
<ModalOverlay />
|
||||
<ModalContent maxW={1000}>
|
||||
<ModalHeader fontSize={"md"}>Exit Transaction</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
{/* <Text as="label" mb="5px" fontSize="sm" fontWeight={500}>
|
||||
Amount to Distribute
|
||||
</Text> */}
|
||||
<HStack
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
as={"form"}
|
||||
mb={4}
|
||||
alignItems={"center"}
|
||||
>
|
||||
{/* <Input placeholder="$00.00" size={"sm"} className="col" /> */}
|
||||
{/* <FormControl isInvalid={errors.amount} isRequired>*/}
|
||||
<Text textAlign={"right"} fontSize={"sm"}>
|
||||
Exit Amount :
|
||||
</Text>
|
||||
<Text
|
||||
textAlign={"start"}
|
||||
bg={"green.100"}
|
||||
p={2}
|
||||
rounded={"md"}
|
||||
fontSize={"sm"}
|
||||
pt={1}
|
||||
pb={1}
|
||||
fontWeight={600}
|
||||
>
|
||||
${" "}
|
||||
{parseFloat(IODetails?.ioMVNAV || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}
|
||||
</Text>
|
||||
|
||||
{/* </FormControl> */}
|
||||
</HStack>
|
||||
|
||||
{/* {calcualtedData && ( */}
|
||||
<NormalData
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
// total={<Total />}
|
||||
// isLoading={isLoading}
|
||||
/>
|
||||
{/* ) } */}
|
||||
</ModalBody>
|
||||
{!isMaker() && (
|
||||
<ModalFooter pt={0}>
|
||||
<Box display={"flex"} justifyContent={"center"} gap={2}>
|
||||
<Button
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={3}
|
||||
py={2}
|
||||
onClick={() => {
|
||||
setActionId(id); // Use the `id` variable from params
|
||||
onConfirmOpen();
|
||||
}}
|
||||
colorScheme="forestGreen"
|
||||
variant={"solid"}
|
||||
cursor={"pointer"}
|
||||
>
|
||||
Approve
|
||||
</Button>
|
||||
<Button
|
||||
rounded={"sm"}
|
||||
size={"xs"}
|
||||
textTransform={"inherit"}
|
||||
fontWeight={500}
|
||||
px={3}
|
||||
py={2}
|
||||
onClick={() => {
|
||||
setActionId(id); // Use the `id` variable from params
|
||||
onRejectOpen();
|
||||
}}
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
</Box>
|
||||
</ModalFooter>
|
||||
)}
|
||||
</ModalContent>
|
||||
<ApprovedExit
|
||||
isOpen={isConfirmOpen}
|
||||
onClose={onConfirmClose}
|
||||
onBigModalClose={onClose}
|
||||
id={investerId}
|
||||
/>
|
||||
<RequestRejectModal
|
||||
isOpen={isRejectOpen}
|
||||
onClose={onRejectClose}
|
||||
onBigModalClose={onClose}
|
||||
id={investerId}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewExit;
|
||||
@@ -33,11 +33,12 @@ import {
|
||||
import ToastBox from "../../../Components/ToastBox";
|
||||
import { getFileNameFromPath } from "../../../Constants/Constants";
|
||||
import { TbFileTypeDocx } from "react-icons/tb";
|
||||
import SetDisplayOrder from "./SetDisplayOrder";
|
||||
import SetDisplayOrder from "./SetDisplayOrderKeyMerits";
|
||||
import SetDisplayOrderIODocuments from "./SetDisplayOrderIODocuments";
|
||||
|
||||
const downloadFile = (filePath, fileName) => {
|
||||
|
||||
fetch("https://tanami.betadelivery.com/" + filePath)
|
||||
fetch(import.meta.env.VITE_IMAGE_URL+filePath)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
@@ -134,6 +135,10 @@ const InvestmentDocument = ({ control, errors, enableNextTab, index, }) => {
|
||||
item?.documentName?.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
const sortedData = filteredData?.sort(
|
||||
(a, b) => a.displayOrder - b.displayOrder
|
||||
);
|
||||
|
||||
const handleView = (id) => {
|
||||
setActionId(id);
|
||||
onViewOpen();
|
||||
@@ -168,7 +173,7 @@ const InvestmentDocument = ({ control, errors, enableNextTab, index, }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const extractedArray = filteredData?.map((item, index) => ({
|
||||
const extractedArray = sortedData?.map((item, index) => ({
|
||||
"Sr.no": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
@@ -300,7 +305,7 @@ const InvestmentDocument = ({ control, errors, enableNextTab, index, }) => {
|
||||
<Box display="flex" justifyContent="end" mb={4} gap={2}>
|
||||
|
||||
|
||||
{filteredData?.length !== 0 &&<SetDisplayOrder data={filteredData} />}
|
||||
{filteredData?.length !== 0 &&<SetDisplayOrderIODocuments data={filteredData} />}
|
||||
<Button
|
||||
leftIcon={<AddIcon />}
|
||||
onClick={onOpen}
|
||||
@@ -358,7 +363,7 @@ const InvestmentDocument = ({ control, errors, enableNextTab, index, }) => {
|
||||
<CustomAlertDialog
|
||||
onClose={() => setDeleteAlert(false)}
|
||||
isOpen={deleteAlert}
|
||||
message="Are you sure you want to delete the sponsor?"
|
||||
message="Are you sure you want to delete the Investment?"
|
||||
alertHandler={handleDelete}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
Thead,
|
||||
Tooltip,
|
||||
Tr,
|
||||
keyframes,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
@@ -37,27 +38,32 @@ import { formatCurrency } from "../../../Components/CurrencyInput";
|
||||
import { FiRefreshCw } from "react-icons/fi";
|
||||
import { useGetIOByIdQuery } from "../../../Services/io.service";
|
||||
import { RepeatIcon } from "@chakra-ui/icons";
|
||||
import { exportToExcel, exportToExcelNew } from "../../../Constants/Constants";
|
||||
import { LuFileSpreadsheet } from "react-icons/lu";
|
||||
|
||||
const rotate = keyframes`
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
`;
|
||||
|
||||
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
|
||||
|
||||
const Investors = ({data}) => {
|
||||
const params = useParams()
|
||||
const id = params?.id
|
||||
const Investors = ({ data }) => {
|
||||
const params = useParams();
|
||||
const id = params?.id;
|
||||
const toast = useToast();
|
||||
const { investors, setInvestors, slideFromRight, IODetails } =
|
||||
useContext(GlobalStateContext);
|
||||
|
||||
const [isRefetchLoading, setIsRefetchLoading] = useState(false);
|
||||
|
||||
console.log(params?.id);
|
||||
|
||||
const {
|
||||
data: IObyID,
|
||||
isLoading: IObyIDisLoading,
|
||||
error: IObyIDerror,
|
||||
refetch
|
||||
} = useGetIOByIdQuery(id, { skip: !id });
|
||||
|
||||
|
||||
const { isLoading: IObyIDisLoading, refetch } = useGetIOByIdQuery(id, {
|
||||
skip: !id,
|
||||
});
|
||||
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
@@ -94,19 +100,19 @@ const Investors = ({data}) => {
|
||||
0
|
||||
);
|
||||
|
||||
// Table setup
|
||||
// Table setup
|
||||
const tableHeadRow = [
|
||||
"Client ID",
|
||||
"First name",
|
||||
"Last name",
|
||||
"Investment amount",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Investment Amount",
|
||||
"Percentage",
|
||||
"Market Value",
|
||||
"Return on Investment",
|
||||
"Distribution",
|
||||
"Distribution Percent",
|
||||
"Total Return",
|
||||
"Total return on Investment",
|
||||
"Total Return on Investment",
|
||||
];
|
||||
|
||||
const handleUpdateStatus = debounce((id) => {
|
||||
@@ -120,16 +126,48 @@ const Investors = ({data}) => {
|
||||
});
|
||||
}, 300);
|
||||
|
||||
|
||||
|
||||
// Table filter
|
||||
const filteredData = IODetails?.investors?.filter((item) => {
|
||||
const clientId = item?.clientReference_id;
|
||||
const name = item.firstName;
|
||||
const lastName = item?.lastName
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
const nameMatches = name.toLowerCase().includes(searchLower);
|
||||
const nameMatches = name.toLowerCase().includes(searchLower) || lastName.toLowerCase().includes(searchLower) || clientId.toLowerCase().includes(searchLower);
|
||||
return nameMatches;
|
||||
});
|
||||
|
||||
|
||||
|
||||
const customHeaders = [
|
||||
{ label: "Client ID", key: "clientReference_id" },
|
||||
{ label: "First Name", key: "firstName" }, // Nested property
|
||||
{ label: "Last Name", key: "lastName" }, // Nested property
|
||||
{ label: "Investment amount", key: "InvestedAmount_USD" }, // Nested property
|
||||
{ label: "Percentage", key: "Investor_Holidings" }, // Nested property
|
||||
{ label: "Market Value", key: "Market_Value" }, // Nested property
|
||||
{ label: "Return on Investment", key: "Return_On_Investment" }, // Nested property
|
||||
{ label: "Distribution", key: "Distribution_Amt" }, // Simple property
|
||||
{ label: "Distribution Percent", key: "Distribution_Per" }, // Simple property // Simple property
|
||||
{ label: "Total Return", key: "Total_Return" }, // Simple property
|
||||
{ label: "Total return on Investment", key: "Total_Return_On_Investment" },
|
||||
];
|
||||
|
||||
const exportInvestorDetails = IODetails?.investors?.map((item, index) => ({
|
||||
"Client ID": item?.clientReference_id, // Keep as string
|
||||
"First Name": item?.firstName,
|
||||
"Last Name": item?.lastName,
|
||||
"Investment Amount": parseFloat(item?.InvestedAmount_USD) || 0, // Convert to float
|
||||
"Percentage": parseFloat(item?.Investor_Holidings) || 0, // Convert to float
|
||||
"Market Value": parseFloat(item?.Market_Value) || 0, // Convert to float
|
||||
"Return on Investment": parseFloat(item?.Return_On_Investment) || 0, // Convert to float
|
||||
"Distribution": parseFloat(item?.Distribution_Amt) || 0, // Convert to float
|
||||
"Distribution Percent": parseFloat(item?.Distribution_Per) || 0, // Convert to float
|
||||
"Total Return": parseFloat(item?.Total_Return) || 0, // Convert to float
|
||||
"Total Return on Investment": parseFloat(item?.Total_Return_On_Investment) || 0, // Convert to float
|
||||
}));
|
||||
|
||||
console.log(exportInvestorDetails);
|
||||
|
||||
const extractedArray = filteredData?.map((item, index) => ({
|
||||
id: item?.id,
|
||||
@@ -144,7 +182,7 @@ const Investors = ({data}) => {
|
||||
{item?.clientReference_id}
|
||||
</Text>
|
||||
),
|
||||
"First name": (
|
||||
"First Name": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
as={"span"}
|
||||
@@ -155,7 +193,7 @@ const Investors = ({data}) => {
|
||||
{item.firstName}
|
||||
</Text>
|
||||
),
|
||||
"Last name": (
|
||||
"Last Name": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
as={"span"}
|
||||
@@ -166,19 +204,25 @@ const Investors = ({data}) => {
|
||||
{item.lastName}
|
||||
</Text>
|
||||
),
|
||||
"Investment amount": (
|
||||
"Investment Amount": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{/* {`$${formatCurrency(item.InvestedAmount_USD)}`} */}
|
||||
{`$${parseFloat(item.InvestedAmount_USD||0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`}
|
||||
{`${parseFloat(item.InvestedAmount_USD || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}`}
|
||||
</Text>
|
||||
),
|
||||
"Percentage": (
|
||||
Percentage: (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
as={"span"}
|
||||
@@ -191,13 +235,19 @@ const Investors = ({data}) => {
|
||||
),
|
||||
"Market Value": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{`$${parseFloat(item.Market_Value ||0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`}
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{`${parseFloat(item.Market_Value || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}`}
|
||||
</Text>
|
||||
),
|
||||
"Return on Investment": (
|
||||
@@ -212,16 +262,22 @@ const Investors = ({data}) => {
|
||||
{item.Return_On_Investment || 0} %
|
||||
</Text>
|
||||
),
|
||||
"Distribution": (
|
||||
Distribution: (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{/* {`$${item.Distribution_Amt}`} */}
|
||||
{`$${parseFloat(item.Distribution_Amt||0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`}
|
||||
{`${parseFloat(item.Distribution_Amt || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}`}
|
||||
</Text>
|
||||
),
|
||||
"Distribution Percent": (
|
||||
@@ -233,22 +289,31 @@ const Investors = ({data}) => {
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{/* {`$${item.Distribution_Amt}`} */}
|
||||
{`${parseFloat(item.Distribution_Per||0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} %`}
|
||||
{`${parseFloat(item.Distribution_Per || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})} %`}
|
||||
</Text>
|
||||
),
|
||||
"Total Return": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
justifyContent={slideFromRight ? "right" : "left"}
|
||||
as={"span"}
|
||||
color={"teal.900"}
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
<Badge ms={1} colorScheme="green" me={1}>
|
||||
$
|
||||
</Badge>
|
||||
{/* {`$${formatCurrency(item.Total_Return) || 0}`} */}
|
||||
{`$${parseFloat(item.Total_Return||0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`}
|
||||
{`${parseFloat(item.Total_Return || 0).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
})}`}
|
||||
</Text>
|
||||
),
|
||||
"Total return on Investment": (
|
||||
"Total Return on Investment": (
|
||||
<Text
|
||||
justifyContent={slideFromRight ? "right" : "center"}
|
||||
as={"span"}
|
||||
@@ -256,7 +321,7 @@ const Investors = ({data}) => {
|
||||
fontWeight={"500"}
|
||||
className="d-flex align-items-center web-text-small"
|
||||
>
|
||||
{item.Total_Return_On_Investment||0} %
|
||||
{item.Total_Return_On_Investment || 0} %
|
||||
</Text>
|
||||
),
|
||||
}));
|
||||
@@ -278,7 +343,7 @@ const Investors = ({data}) => {
|
||||
return (
|
||||
<Table size="sm">
|
||||
<Tbody backgroundColor="gray.50">
|
||||
<Tr >
|
||||
<Tr>
|
||||
<Th
|
||||
textAlign={"center"}
|
||||
p={3}
|
||||
@@ -395,10 +460,16 @@ const Investors = ({data}) => {
|
||||
);
|
||||
};
|
||||
|
||||
const handleRefresh = async () => {
|
||||
setIsRefetchLoading(true);
|
||||
|
||||
await refetch();
|
||||
setIsRefetchLoading(false);
|
||||
};
|
||||
|
||||
|
||||
console.log(IODetails?.investors);
|
||||
|
||||
const handleRefresh = () =>{
|
||||
refetch()
|
||||
}
|
||||
|
||||
return (
|
||||
<Box {...OPACITY_ON_LOAD} pb={0}>
|
||||
@@ -408,26 +479,82 @@ const Investors = ({data}) => {
|
||||
justifyContent={"space-between"}
|
||||
pb={3}
|
||||
spacing="24px"
|
||||
|
||||
>
|
||||
<span>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
<Icon ms={3} bg={"gray.100"} onClick={handleRefresh} fontWeight={600} as={RepeatIcon} boxSize={8} p={2} rounded={'md'} _hover={{bg:'gray.100'}} cursor={'pointer'} />
|
||||
</span>
|
||||
<Box display={'flex'} gap={3}>
|
||||
<Input
|
||||
type="search"
|
||||
width={300}
|
||||
placeholder="Search..."
|
||||
size="sm"
|
||||
rounded="sm"
|
||||
focusBorderColor="green.500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
|
||||
<HStack bg={'#C6F6D5'} ps={4} pe={4} pt={1.5} pb={1.5} rounded={'md'} boxShadow={'sm'} display={"flex"} alignItems={"end"} flexDirection={'column'} >
|
||||
<Text fontWeight={600} fontSize={'sm'} as={'span'}>$ {parseFloat(IODetails?.totalAmtInvestmentInUSD).toLocaleString()}</Text>
|
||||
<Text fontWeight={600} color={'gray.500'} fontSize={'xs'} as={'span'}>Total Investment Amount ( USD )</Text>
|
||||
<Button
|
||||
onClick={() =>
|
||||
exportToExcelNew(exportInvestorDetails, "IO Investors Details")
|
||||
}
|
||||
leftIcon={<LuFileSpreadsheet />}
|
||||
colorScheme="forestGreen"
|
||||
size={"sm"}
|
||||
variant={"outline"}
|
||||
rounded={"sm"}
|
||||
fontSize={"xs"}
|
||||
w={100}
|
||||
me={2}
|
||||
isDisabled={exportInvestorDetails?.length === 0}
|
||||
>
|
||||
Export xls
|
||||
</Button>
|
||||
{/* <Box as="span">
|
||||
<Icon
|
||||
ms={0}
|
||||
animation={
|
||||
isRefetchLoading ? `${rotate} 1s linear infinite` : "none"
|
||||
}
|
||||
bg={"gray.50"}
|
||||
onClick={handleRefresh}
|
||||
fontWeight={600}
|
||||
as={RepeatIcon}
|
||||
boxSize={8}
|
||||
p={2}
|
||||
rounded={"full"}
|
||||
_hover={{ bg: "gray.100" }}
|
||||
cursor={"pointer"}
|
||||
/>
|
||||
</Box> */}
|
||||
</Box>
|
||||
|
||||
<HStack
|
||||
bg={"#C6F6D5"}
|
||||
ps={4}
|
||||
pe={4}
|
||||
pt={1.5}
|
||||
pb={1.5}
|
||||
rounded={"md"}
|
||||
boxShadow={"sm"}
|
||||
display={"flex"}
|
||||
// alignItems={"end"}
|
||||
// flexDirection={"column"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Text
|
||||
fontWeight={600}
|
||||
color={"gray.500"}
|
||||
fontSize={"xs"}
|
||||
as={"span"}
|
||||
>
|
||||
Total Investment Amount ( USD )
|
||||
</Text>
|
||||
|
||||
<Text display={'flex'} alignItems={'center'} fontWeight={600} fontSize={"sm"} as={"span"} pt={"2px"}>
|
||||
<Badge p={1} ms={2} fontSize={'md'} colorScheme="green" me={0}>
|
||||
$
|
||||
</Badge>
|
||||
{parseFloat(IODetails?.totalAmtInvestmentInUSD).toLocaleString()}
|
||||
</Text>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
@@ -436,7 +563,7 @@ const Investors = ({data}) => {
|
||||
centered={true}
|
||||
emptyMessage={`We don't have any Sponers `}
|
||||
tableHeadRow={tableHeadRow}
|
||||
data={extractedArray}
|
||||
data={extractedArray}
|
||||
isLoading={isLoading}
|
||||
viewActionId={actionId}
|
||||
setViewActionId={setActionId}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user