Compare commits

..

73 Commits

Author SHA1 Message Date
YasinShaikh123
85be7c891e update bugs 2024-12-20 17:17:18 +05:30
YasinShaikh123
e41d6b17b9 update env file ang bugs 2024-12-20 16:22:29 +05:30
Swapnil Bendal
5411d4cd18 [fixed] - email message forget password 2024-12-12 17:47:57 +05:30
Swapnil Bendal
6a0aa17e2d [update]- profile page 2024-12-12 17:28:32 +05:30
Swapnil Bendal
471e4f32ab [fixed] - token server 2024-12-12 16:19:36 +05:30
YasinShaikh123
5d2c28f6ca update change password 2024-12-12 13:52:55 +05:30
YasinShaikh123
49f39e1c2c update subadmin 2024-12-12 13:39:19 +05:30
YasinShaikh123
f9c7bf9d5d update forget passsword 2024-12-12 13:20:12 +05:30
Swapnil Bendal
49beb9539a [fixed] - role 2024-12-11 20:30:52 +05:30
Swapnil Bendal
ed27ed6939 [update] - io cash, IO nav and pending 2024-12-11 19:36:06 +05:30
Swapnil Bendal
d84b3a0e35 [update] - role check 2024-12-11 18:47:43 +05:30
Swapnil Bendal
20c0c7840f [update] - constant 2024-12-11 15:45:56 +05:30
Swapnil Bendal
bbfd617b27 [update] - finalise 2024-12-11 14:54:13 +05:30
Swapnil Bendal
87e0716383 [added] - .env.exmple 2024-12-11 13:21:43 +05:30
Swapnil Bendal
c7d6a0fe36 [update] - token service 2024-12-11 12:59:19 +05:30
Swapnil Bendal
c83aaa411a Merge remote-tracking branch 'origin/Sprint-9' into audit-fix/swapnil 2024-12-11 11:42:24 +05:30
Swapnil Bendal
a2fe1435cb [update] - token service 2024-12-11 11:41:02 +05:30
6a9e0f9908 Merge pull request 'Yasin' (#23) from Yasin into Sprint-9
Reviewed-on: #23
2024-12-10 13:35:07 +00:00
YasinShaikh123
9c0b231e62 update changes 2024-12-09 20:10:47 +05:30
Swapnil Bendal
4ca28fd910 [untested] - code checkout in remote 2024-12-06 20:25:12 +05:30
YasinShaikh123
fed5125660 working create password 2024-12-06 20:04:20 +05:30
YasinShaikh123
8a297d02ef Merge branch 'Siddhesh' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into Yasin 2024-12-06 16:15:10 +05:30
YasinShaikh123
2c8965c16a working chenge password 2024-12-06 16:11:18 +05:30
9d4d5301e5 update 2024-12-06 16:09:36 +05:30
88dc9d14fe Merge pull request 'Yasin' (#22) from Yasin into Sprint-9
Reviewed-on: #22
2024-12-06 07:18:57 +00:00
YasinShaikh123
d567acfec8 update subadmin 2024-12-05 20:26:39 +05:30
YasinShaikh123
463325e603 update investor 2024-12-02 12:47:44 +05:30
84f869d9bd Merge branch 'release/sprint-8' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into Siddhesh 2024-12-02 12:26:26 +05:30
606ac68d85 Merge pull request 'update bug' (#20) from Yasin into release/sprint-8
Reviewed-on: #20
2024-12-02 06:55:15 +00:00
YasinShaikh123
537304f0fb update bug 2024-12-02 12:23:27 +05:30
YasinShaikh123
3b83b625c3 update Investment Details KYC 2024-11-25 20:24:39 +05:30
Swapnil Bendal
694dde0f14 [fixed] - view io details 2024-11-25 19:03:42 +05:30
Swapnil Bendal
4f8916036e [update] - condition on pending request for checkers 2024-11-25 16:45:52 +05:30
YasinShaikh123
0c21e99732 update comment changes 2024-11-25 16:41:19 +05:30
YasinShaikh123
5900f637be Merge branch 'release/sprint-8' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into Yasin 2024-11-25 15:52:33 +05:30
YasinShaikh123
2453b24d91 Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into Yasin 2024-11-25 12:21:02 +05:30
Swapnil Bendal
3db9488444 [update] - counter in pending tab 2024-11-22 20:25:21 +05:30
bb30a71a60 Merge pull request 'modal api useEffect' (#19) from Yasin into release/sprint-8
Reviewed-on: #19
2024-11-22 14:27:49 +00:00
YasinShaikh123
526d2aecca modal api useEffect 2024-11-22 19:57:05 +05:30
e742f6c18d Merge pull request 'update modal view' (#18) from Yasin into release/sprint-8
Reviewed-on: #18
2024-11-22 14:24:42 +00:00
YasinShaikh123
d4c9a5521f update modal view 2024-11-22 19:54:12 +05:30
7a04ee0abb Merge pull request 'updaste' (#17) from Yasin into release/sprint-8
Reviewed-on: #17
2024-11-22 13:53:09 +00:00
YasinShaikh123
a12b2c9a2c updaste 2024-11-22 19:22:06 +05:30
0b2e48200a Merge pull request 'token change' (#16) from Yasin into release/sprint-8
Reviewed-on: #16
2024-11-22 13:35:11 +00:00
YasinShaikh123
bf7b3e1596 token change 2024-11-22 19:04:31 +05:30
059b711bc1 Merge branch 'release/sprint-8' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into release/sprint-8 2024-11-22 19:00:15 +05:30
b0c01f3a0c updaate role 2024-11-22 19:00:14 +05:30
272e94caf0 upodate 2024-11-22 18:57:49 +05:30
cd95aa35a7 Merge branch 'Yasin' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into Siddhesh 2024-11-22 18:53:22 +05:30
973ec656d0 Merge pull request 'update modal close' (#15) from Yasin into release/sprint-8
Reviewed-on: #15
2024-11-22 13:22:09 +00:00
YasinShaikh123
29a49150b7 update modal close 2024-11-22 18:51:05 +05:30
7a908a3284 Merge pull request 'updated' (#14) from Yasin into release/sprint-8
Reviewed-on: #14
2024-11-22 11:28:53 +00:00
YasinShaikh123
23c3997d06 updated 2024-11-22 16:56:31 +05:30
6779fdd672 Merge pull request 'Yasin' (#13) from Yasin into release/sprint-8
Reviewed-on: #13
2024-11-22 10:50:46 +00:00
YasinShaikh123
0ab898a3da update new bugs 2024-11-22 16:19:59 +05:30
YasinShaikh123
f3a8a80f4d update action 2024-11-22 13:32:28 +05:30
d1cbeee294 Merge pull request 'update bugs' (#12) from Yasin into release/sprint-8
Reviewed-on: #12
2024-11-22 07:22:50 +00:00
YasinShaikh123
1c4f975781 update bugs 2024-11-22 12:48:02 +05:30
17ffb864a3 Merge pull request 'Yasin' (#11) from Yasin into release/sprint-8
Reviewed-on: #11
2024-11-21 12:14:25 +00:00
YasinShaikh123
6ffcff58a2 update tabs 2024-11-21 17:42:27 +05:30
82612fa7f2 Merge pull request 'update color bugs' (#10) from main into release/sprint-8
Reviewed-on: #10
2024-11-21 10:36:20 +00:00
YasinShaikh123
470ba49c00 UPDATE invest modal 2024-11-21 16:05:51 +05:30
b29478a939 Merge pull request 'transaction panding' (#9) from Yasin into release/sprint-8
Reviewed-on: #9
2024-11-21 10:22:35 +00:00
YasinShaikh123
7ba524d2e4 transaction panding 2024-11-21 15:51:01 +05:30
cc58cdc9b7 Merge pull request 'update invest amount' (#8) from Yasin into release/sprint-8
Reviewed-on: #8
2024-11-21 10:17:31 +00:00
YasinShaikh123
9f54bfbc65 update invest amount 2024-11-21 15:46:10 +05:30
992cb13e1e Merge pull request 'update transaction status modal' (#7) from Yasin into release/sprint-8
Reviewed-on: #7
2024-11-19 13:53:15 +00:00
YasinShaikh123
85e3c34120 update transaction status modal 2024-11-19 19:19:45 +05:30
46672a34e2 Merge pull request 'update Io Cash Nav Transaction' (#6) from Yasin into release/sprint-8
Reviewed-on: #6
2024-11-18 13:36:45 +00:00
YasinShaikh123
61e49393fe update Io Cash Nav Transaction 2024-11-18 17:54:23 +05:30
YasinShaikh123
5fc16b58ea working tabs👷‍♂️ 2024-11-14 16:04:20 +05:30
95c629533e update 2024-11-14 12:18:54 +05:30
YasinShaikh123
06548abf1e update tabs 2024-11-14 12:08:17 +05:30
98 changed files with 10708 additions and 1106 deletions

28
.env.example Normal file
View File

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

View File

@@ -63,8 +63,8 @@ const App = () => {
path="/*"
element={
// isOnline ? (
// isAuthenticate || isAuthenticatedInCookie === "true" ? (
localStorage.getItem('accessToken') && localStorage.getItem('refreshToken') ? (
isAuthenticate || isAuthenticatedInCookie === "true" ? (
// localStorage.getItem('accessToken') && localStorage.getItem('refreshToken') ? (
// true ? (
<DefaultLayout isOnline={isOnline} />
) : (

View File

@@ -13,8 +13,9 @@ import {
Portal,
Text,
useColorMode,
useDisclosure,
} from "@chakra-ui/react";
import React, { useContext } from "react";
import React, { useContext, useRef } from "react";
import { Link, useNavigate } from "react-router-dom";
import { IoMdDownload } from "react-icons/io";
import * as XLSX from "xlsx";
@@ -23,6 +24,7 @@ import GlobalStateContext from "../Contexts/GlobalStateContext";
import { MdOutlineDarkMode, MdOutlineLightMode } from "react-icons/md";
import logoMini from "../assets/propic.png";
import { BsBack } from "react-icons/bs";
import ChangePassword from "../Pages/ChangePassword";
const HeaderMain = ({
link,
@@ -35,6 +37,8 @@ const HeaderMain = ({
}) => {
const navigate = useNavigate();
const { colorMode, toggleColorMode } = useContext(GlobalStateContext);
const { isOpen, onOpen, onClose } = useDisclosure();
const firstField = useRef();
return (
<Box
@@ -66,11 +70,11 @@ const HeaderMain = ({
<PopoverBody onClick={()=> navigate('/profile')} className="web-text-medium pointer link">
Profile
</PopoverBody>
<Link to={"/help-and-support"}>
<Box onClick={onOpen}>
<PopoverBody className="web-text-medium pointer ">
Help & Support
Change Password
</PopoverBody>
</Link>
</Box>
<PopoverFooter
onClick={logOutHandler}
className="web-text-medium pointer link"
@@ -112,6 +116,11 @@ const HeaderMain = ({
{/* <Box onClick={() => toggleColorMode()} as="span" p={2} rounded={'lg'} className="link pointer">
{colorMode === "light"? <MdOutlineDarkMode /> :<MdOutlineLightMode />}
</Box> */}
<ChangePassword
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
</Box>
</Box>
);

View File

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

View File

@@ -1,19 +1,31 @@
import React from "react";
import './FullscreenLoaders.css'
import { Spinner } from "@chakra-ui/react";
const Loader01 = () => {
return (
// <div className="dot-spinner">
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// </div>
<div className="dot-spinner">
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
</div>
// <div className="dot-spinner">
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// <div className="dot-spinner__dot"></div>
// </div>
<Spinner color='green.900' />
);
};

View File

@@ -5,7 +5,7 @@ import { ChevronLeftIcon, ChevronRightIcon } from "@chakra-ui/icons";
const Pagination = ({
pageSize,
setPageSize,
totalItems,
totalItems = 1,
isLoading,
setCurrentPage,
currentPage,
@@ -84,7 +84,7 @@ const Pagination = ({
onClick={paginationNext}
className="link pointer"
isDisabled={currentPage === totalPages}
aria-label="Next Page"
aria-label="Next Page"
/>
</HStack>
</HStack>

View File

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

View File

@@ -1,9 +1,9 @@
import dns from "node:dns"
import * as XLSX from 'xlsx';
import CryptoJS from "crypto-js";
export const generateSerialNumber = (index, currentPage, pageSize) => {
export const generateSerialNumber = (index, currentPage = 1, pageSize = 1) => {
return (currentPage - 1) * pageSize + (index + 1);
};
@@ -11,7 +11,7 @@ export function getTomorrowDate() {
const today = new Date();
const tomorrow = new Date(today);
tomorrow.setDate(today.getDate() + 1);
// Format the date as YYYY-MM-DD (ISO 8601)
return tomorrow.toISOString().split('T')[0];
}
@@ -33,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();
@@ -56,7 +56,7 @@ export function removeTrailingZeros(value) {
const remainingMinutes = minutes % 60;
const remainingSeconds = seconds % 60;
return `${remainingDays === 0 ? "": remainingDays+"d"} ${remainingHours === 0 ? "": remainingHours+"h"} ${remainingMinutes}m ${remainingSeconds}s `;
return `${remainingDays === 0 ? "" : remainingDays + "d"} ${remainingHours === 0 ? "" : remainingHours + "h"} ${remainingMinutes}m ${remainingSeconds}s `;
}
@@ -94,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
}
}
@@ -125,15 +128,15 @@ export async function checkEmailValidity(email) {
// Function to convert timestamp to readable date format in Gulf timezone
export function formatTimestampInGulfTimezone(timestamp) {
const date = new Date(timestamp);
const options = {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZone: 'Asia/Dubai', // Gulf Standard Time (GST) timezone
timeZoneName: 'short'
const options = {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZone: 'Asia/Dubai', // Gulf Standard Time (GST) timezone
timeZoneName: 'short'
};
return date.toLocaleDateString('en-GB', options);
}
@@ -163,7 +166,7 @@ const getNestedValue = (obj, key) => {
export const exportToExcel = (data, headers) => {
const flattenedData = data.map((item) => {
const newItem = {};
// Loop through customHeaders and get the correct values
headers.forEach((header) => {
newItem[header.label] = getNestedValue(item, header.key); // Use the helper function
@@ -174,7 +177,7 @@ export const exportToExcel = (data, headers) => {
// Now pass flattenedData to your Excel library to generate the file
// Assuming you're using a library like `xlsx` for this part:
const worksheet = XLSX.utils.json_to_sheet(flattenedData);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
@@ -218,4 +221,25 @@ export function formatDateToYYYYMMDD(dateString) {
// Combine the formatted parts
return `${year}-${month}-${day}`;
}
}
// Encrypt a string
export const encryptString = (text) => {
const ciphertext = CryptoJS.AES.encrypt(text, import.meta.env.VITE_ROLE_ENCRYPTION_KEY).toString();
return ciphertext;
};
// Decrypt a string
export const decryptString = (ciphertext) => {
const bytes = CryptoJS.AES.decrypt(ciphertext, import.meta.env.VITE_ROLE_ENCRYPTION_KEY);
const originalText = bytes.toString(CryptoJS.enc.Utf8);
return originalText;
};
export const SUPER_ADMIN_ID = Number(import.meta.env.VITE_SUPER_ADMIN_ID) || 1
export const MAKER_ID = import.meta.env.VITE_MAKER_ID || 1
export const CHECKER_ID = import.meta.env.VITE_CHECKER_ID || 2
export const isMaker = (role = decryptString(localStorage?.getItem("role"))) => role === import.meta.env.VITE_MAKER;
export const isChecker = (role = decryptString(localStorage?.getItem("role"))) => role === import.meta.env.VITE_CHECKER;

View File

@@ -1,2 +1,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

View File

@@ -1557,6 +1557,123 @@ const GlobalStateProvider = ({ children }) => {
},
]);
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 ]===============================
@@ -1643,7 +1760,13 @@ const GlobalStateProvider = ({ children }) => {
fawateerRequest,
setFawateerRequest,
approveHistory,
setApproveHistory
setApproveHistory,
approved,
setApproved,
iONAVDetail,
setIONAVDetail,
iOTransaction,
setIOTransaction,
}}
>
{children}

View File

@@ -1,21 +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 { useDispatch } from "react-redux";
import { loginUser } from "../Redux/Slice/auth";
import Button02 from "../Components/Buttons/Button02";
import { CgProfile } from "react-icons/cg";
import { useDispatch } from "react-redux";
import logoMiniDark from "../assets/favicon.png";
import logoMini from "../assets/logo-min.png";
import logoDark from "../assets/logo.png";
import logo from "../assets/logo2.png";
import {
TbArrowBadgeLeftFilled,
ArrowBackIcon,
ArrowLeftIcon,
ArrowRightIcon,
AtSignIcon,
} from "@chakra-ui/icons";
import {
Accordion,
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
Alert,
AlertIcon,
Box,
Button,
Image,
Text,
Tooltip
} from "@chakra-ui/react";
import Cookies from "js-cookie"; // Import the Cookies library
import { GrManual } from "react-icons/gr";
import { HiOutlineChartSquareBar } from "react-icons/hi";
import { LuContact } from "react-icons/lu";
import { MdNotificationsNone, MdOutlineAddChart } from "react-icons/md";
import {
RiBankLine,
RiExchangeBoxLine,
RiFileUserLine,
RiMoneyDollarBoxLine,
} from "react-icons/ri";
import {
TbListDetails,
TbReportMoney,
TbTransactionDollar,
TbTransactionDollar
} from "react-icons/tb";
import { TbArrowBadgeRightFilled } from "react-icons/tb";
import { ArrowBackIcon, ArrowLeftIcon, ArrowRightIcon, AtSignIcon } from "@chakra-ui/icons";
import { VscSymbolClass } from "react-icons/vsc";
import {
Link,
NavLink,
@@ -24,70 +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,
Tooltip,
useRadio,
} from "@chakra-ui/react";
import GlobalStateContext from "../Contexts/GlobalStateContext";
import Cookies from "js-cookie"; // Import the Cookies library
import Header from "../Components/Header";
import HeaderMain from "../Components/HeaderMain";
import { IoMdSwap } from "react-icons/io";
import {
RiBankLine,
RiExchangeBoxLine,
RiFileUserLine,
RiMoneyDollarBoxLine,
} from "react-icons/ri";
import { VscSymbolClass } from "react-icons/vsc";
import { MdNotificationsNone, MdOutlineAddChart } from "react-icons/md";
import { HiOutlineChartSquareBar } from "react-icons/hi";
import { GrManual } from "react-icons/gr";
import { LuContact } from "react-icons/lu";
import shield from "../assets/shield.png";
import SplashScreen from "../Pages/SplashScreen";
import CutomBreadcrumb from "../Components/CutomBreadcrumb";
import CustomBreadcrumb from "../Components/CutomBreadcrumb";
import { getCountdownTimer } from "../Constants/Constants";
import { useLogoutMutation } from "../Services/token.serivce";
import GlobalStateContext from "../Contexts/GlobalStateContext";
import CreateRequest from "../Pages/Fawateer/CreateRequest";
import ApproveRequest from "../Pages/FawateerChecker/ApproveRequest/ApproveRequest";
import ApproveHistoryMaker from "../Pages/FawateerChecker/ApproveHistory/ApproveHistoryMaker";
import ApproveHistory from "../Pages/FawateerChecker/ApproveHistory/ApproveHistoryChecker";
import ApproveHistoryMaker from "../Pages/FawateerChecker/ApproveHistory/ApproveHistoryMaker";
import ApproveRequest from "../Pages/FawateerChecker/ApproveRequest/ApproveRequest";
import NotFound from "../Pages/NotFound";
import SplashScreen from "../Pages/SplashScreen";
import { nav } from "../Routes/Nav";
import { RouteLink } from "../Routes/Routes";
import { useProfileQuery } from "../Services/io.service";
import { useLogoutMutation } from "../Services/token.serivce";
const DashboardLayout = ({ isOnline }) => {
const userRole = localStorage.getItem("role");
const navigate = useNavigate();
const dispach = useDispatch();
const location = useLocation();
const path = location.pathname;
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
@@ -104,15 +82,15 @@ const DashboardLayout = ({ isOnline }) => {
const { data, refetch } = useProfileQuery();
useEffect(() => {
if (
!localStorage.getItem("accessToken") &&
!localStorage.getItem("refreshToken")
) {
logOutHandler();
return navigate("/login");
}
}, []);
// useEffect(() => {
// if (
// !localStorage.getItem("accessToken") &&
// !localStorage.getItem("refreshToken")
// ) {
// logOutHandler();
// return navigate("/login");
// }
// }, []);
useEffect(() => {
const savedIndex = localStorage.getItem("openAccordionIndex");
@@ -151,7 +129,9 @@ const DashboardLayout = ({ isOnline }) => {
await logout();
localStorage.clear();
navigate("/login");
} catch (error) {}
} catch (error) {
console.log(error);
}
};
// // Function to get the title based on the route
@@ -165,24 +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("/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("/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">
@@ -231,17 +211,17 @@ const DashboardLayout = ({ isOnline }) => {
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
Deposit 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 request
Deposite Request
</span>
);
case path.startsWith("/fawateer"):
return (
<span className="d-flex align-items-end gap-2">
@@ -249,13 +229,13 @@ const DashboardLayout = ({ isOnline }) => {
Fawateer Deposit
</span>
);
case path.startsWith("/fawateer-history"):
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
Fawateer Deposit
</span>
);
case path.startsWith("/fawateer-history"):
return (
<span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
Fawateer Deposit
</span>
);
case path.startsWith("/withdraw-request"):
return (
@@ -369,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/")) {
@@ -398,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={{
@@ -515,7 +515,7 @@ 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"}>
@@ -788,6 +788,7 @@ const AppContent = ({ data }) => {
)
}
/>
<Route path="*" element={<NotFound />} />
</Routes>
);

View File

@@ -49,7 +49,7 @@ const InvestorComment = ({ isOpen, onClose }) => {
fontSize="sm"
type="textarea"
size="md"
placeholder={"Enter your comments...."}
placeholder={"Enter your comment...."}
rounded={"md"}
resize={"none"}
/>

View File

@@ -1,27 +1,25 @@
import {
Avatar,
Badge,
Box,
HStack,
Input,
Select,
Switch,
Text,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState, useRef } from "react";
import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import { debounce } from "../../../Master/Sponser/AddSponser";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import React, { useContext, useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import Pagination from "../../../../Components/Pagination";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import ToastBox from "../../../../Components/ToastBox";
import ReasonBanModal from "./ReasonBanModal";
import { useGetbanInvestorQuery } from "../../../../Services/ban.investor.service";
import { TABLE_PAGINATION } from "../../../../Constants/Paginations";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import { useGetbanInvestorQuery } from "../../../../Services/ban.investor.service";
import { debounce } from "../../../Master/Sponser/AddSponser";
import ReasonBanModal from "./ReasonBanModal";
import Pagination from "../../../../Components/Pagination";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
@@ -233,8 +231,6 @@ const BankInvestor = () => {
),
}));
console.log(extractedArray);
const handleDelete = () => {
const updatedInvestorDetails = InvestorDetails.filter(
(sponsor) => sponsor.id !== actionId
@@ -278,6 +274,14 @@ const BankInvestor = () => {
/>
<HStack display={"flex"} alignItems={"center"}>
<Pagination
isLoading={unbanLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={data?.data?.totalItems}
/>
{/* <Select
focusBorderColor="green.500"
size={"sm"}

View File

@@ -1,31 +1,26 @@
import {
Avatar,
Badge,
Box,
HStack,
Input,
Select,
Switch,
Text,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState, useRef } from "react";
import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import { debounce } from "../../../Master/Sponser/AddSponser";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import DataTable from "../../../../Components/DataTable/NormalTable";
import React, { useContext, useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import Pagination from "../../../../Components/Pagination";
import DataTable from "../../../../Components/DataTable/NormalTable";
import ToastBox from "../../../../Components/ToastBox";
import ReasonBanModal from "./ReasonBanModal";
import {
useGetInvestorQuery,
useGetUnbanInvestorQuery,
} from "../../../../Services/ban.investor.service";
import { generateSerialNumber } from "../../../../Constants/Constants";
import { TABLE_PAGINATION } from "../../../../Constants/Paginations";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import { useGetUnbanInvestorQuery } from "../../../../Services/ban.investor.service";
import { debounce } from "../../../Master/Sponser/AddSponser";
import ReasonBanModal from "./ReasonBanModal";
import Pagination from "../../../../Components/Pagination";
const formatDate = (date) => new Date(date).toLocaleDateString(); // Simple date formatter
@@ -270,6 +265,14 @@ const UnbanInvestor = () => {
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
<Pagination
isLoading={unbanLoading}
pageSize={pageSize}
setPageSize={setPageSize}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
totalItems={data?.data?.totalItems}
/>
{/* <Select
focusBorderColor="green.500"
size={"sm"}

View File

@@ -0,0 +1,234 @@
import {
Button,
DrawerFooter,
FormControl,
FormErrorMessage,
FormLabel,
Input,
InputGroup,
InputRightElement,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalOverlay,
Stack,
useToast,
} from "@chakra-ui/react";
import * as yup from "yup";
import React, { useState, useContext } from "react";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import CustomAlertDialog from "../Components/CustomAlertDialog";
import ToastBox from "../Components/ToastBox";
import { useUpdatePasswordMutation } from "../Services/change.password.service";
import { all } from "axios";
// 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 [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 [updatePassword] = useUpdatePasswordMutation();
// Form handling
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm({
resolver: yupResolver(passwordSchema),
mode: "all",
});
// Form submit handler
const onSubmit = async (data) => {
setIsLoading(true);
try {
const res = await updatePassword(data); // 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);
}
};
// Handle modal close
const handleClose = () => {
setAlert(false);
onClose();
reset();
};
return (
<>
<Modal isOpen={isOpen} onClose={onClose} 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)}
isLoading={isLoading}
>
Save
</Button>
</DrawerFooter>
</ModalContent>
</Modal>
<CustomAlertDialog
isOpen={alert}
onClose={() => setAlert(false)}
alertHandler={handleSubmit(onSubmit)}
message={"Are you sure you want to change the password?"}
isLoading={isLoading}
/>
</>
);
};
export default ChangePassword;

View File

@@ -4,6 +4,7 @@ import {
Button,
FormControl,
FormErrorMessage,
FormHelperText,
FormLabel,
Input,
Modal,
@@ -35,7 +36,7 @@ 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);
@@ -192,7 +193,7 @@ const DepositRequestApprove = ({
)}
</FormControl>
<FormControl mb={4}>
<FormLabel fontSize="sm">Comments</FormLabel>
<FormLabel fontSize="sm">Comment</FormLabel>
<Textarea
rows={5}
focusBorderColor="green.400"
@@ -201,14 +202,19 @@ const DepositRequestApprove = ({
fontSize="sm"
type="textarea"
size="sm"
placeholder={"Enter your comments...."}
placeholder={"Enter your comment...."}
resize={"none"}
maxLength={200}
/>
{errors.comment && (
<Text fontSize="xs" color="red">
{errors.comment.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 200 characters. You have entered </Box>
{watch("comment")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>

View File

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

View File

@@ -1,17 +1,27 @@
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,
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 { 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";
@@ -23,60 +33,67 @@ 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"),
.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'),
.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(),
makerComment: Yup.string() .max(200, "Approve Comment cannot be more than 50 characters"),
});
const CreateRequest = () => {
const toast = useToast()
const navigate=useNavigate()
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)
const [isLoading, setIsLoading] = useState(false);
const [id, setId] = useState(null);
// Initialize useForm with the resolver for Yup validation
const {control, register, handleSubmit, setValue,reset, formState: { errors } } = useForm({
const {
control,
register,
watch,
handleSubmit,
setValue,
reset,
formState: { errors },
} = useForm({
resolver: yupResolver(validationSchema),
});
const [ creatFawaateerRequest ] = useCreateFawateerRequestMutation()
const [creatFawaateerRequest] = useCreateFawateerRequestMutation();
const onSubmit = async (data) => {
console.log(data);
setIsLoading(true)
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
}
});
// 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: () => (
@@ -84,39 +101,35 @@ const CreateRequest = () => {
),
});
setIsLoading(false);
reset()
return
reset();
return;
} else if (res?.data) {
toast({
render: () => (
<ToastBox message={res?.data?.message} />
),
render: () => <ToastBox message={res?.data?.message} />,
});
setIsLoading(false);
navigate('/fawateer-history')
return
navigate("/fawateer-history");
return;
} else {
toast({
render: () => (
<ToastBox status={'error'} message={"Something went wrong"} />
<ToastBox status={"error"} message={"Something went wrong"} />
),
});
setIsLoading(false);
return
return;
}
} catch (error) {
console.error("Error:", error);
toast({
render: () => (
<ToastBox status={'error'} message={"An error occurred"} />
<ToastBox status={"error"} message={"An error occurred"} />
),
});
setIsLoading(false);
return
return;
}
};
// Handle file change and preview
const handleFileChange = (e) => {
@@ -137,15 +150,12 @@ const CreateRequest = () => {
}
};
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
<Box
display={"flex"}
justifyContent={"space-between"}
flexWrap={'wrap'}
flexWrap={"wrap"}
alignItems={"center"}
mt={5}
px={4}
@@ -153,11 +163,16 @@ const CreateRequest = () => {
onSubmit={handleSubmit(onSubmit)}
>
{/* Investor Name Field */}
<FormControl isRequired w={"49%"} mb={2} isInvalid={errors.investorName}>
<FormControl
isRequired
w={"49%"}
mb={2}
isInvalid={errors.investorName}
>
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
Investor name
</FormLabel>
<InputGroup size='sm'>
<InputGroup size="sm">
<Input
bg={"#F5F8F6"}
focusBorderColor="forestGreen.300"
@@ -170,11 +185,24 @@ const CreateRequest = () => {
{...register("investorName")}
_placeholder={{ fontSize: "sm" }}
/>
<InputRightAddon gap={2} color={'forestgreen.400'} onClick={onOpen} cursor={'pointer'} fontWeight={600} fontSize={'xs'}>
<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>
<FormHelperText
fontSize={"xs"}
fontWeight={500}
style={{ color: "red" }}
>
{errors.investorName?.message}
</FormHelperText>
</FormControl>
{/* Client ID Field */}
@@ -193,7 +221,13 @@ const CreateRequest = () => {
placeholder={"Client ID"}
{...register("clientId")}
/>
<FormHelperText fontSize={'xs'} fontWeight={500} style={{ color: "red" }}>{errors.clientId?.message}</FormHelperText>
<FormHelperText
fontSize={"xs"}
fontWeight={500}
style={{ color: "red" }}
>
{errors.clientId?.message}
</FormHelperText>
</FormControl>
{/* Date Field */}
@@ -208,10 +242,21 @@ const CreateRequest = () => {
fontSize={"sm"}
rounded={"sm"}
type={"date"}
max={new Date().toISOString().split("T")[0]} // Disable future dates
{...register("transaction_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>
<FormHelperText
fontSize={"xs"}
fontWeight={500}
style={{ color: "red" }}
>
{errors.transaction_date?.message}
</FormHelperText>
</FormControl>
{/* Amount Field */}
@@ -220,19 +265,35 @@ const CreateRequest = () => {
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" }}>
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>
</FormHelperText>
</FormControl>
{/* Support File Field with Preview */}
<FormControl isRequired w={"49%"} mb={2} isInvalid={errors.spportFile_path}>
<FormControl
isRequired
w={"49%"}
mb={2}
isInvalid={errors.spportFile_path}
>
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
Support file
</FormLabel>
@@ -249,7 +310,13 @@ const CreateRequest = () => {
{...register("spportFile_path")}
// onChange={handleFileChange}
/>
<FormHelperText fontSize={'xs'} fontWeight={500} style={{ color: "red" }}>{errors.spportFile_path?.message}</FormHelperText>
<FormHelperText
fontSize={"xs"}
fontWeight={500}
style={{ color: "red" }}
>
{errors.spportFile_path?.message}
</FormHelperText>
{/* Animated Preview */}
{filePreview && fileType?.type.startsWith("image/") && (
@@ -259,14 +326,55 @@ const CreateRequest = () => {
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>
<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>
@@ -274,7 +382,7 @@ const CreateRequest = () => {
</FormControl>
{/* Description Field */}
<FormControl w={"100%"} mb={2} isInvalid={errors.makerComment}>
<FormControl w={"100%"} mb={2} isInvalid={errors.makerComment}>
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
Description
</FormLabel>
@@ -286,13 +394,26 @@ const CreateRequest = () => {
rounded={"sm"}
placeholder={"Description"}
{...register("makerComment")}
maxLength={200}
/>
<FormHelperText fontSize={'xs'} fontWeight={500} style={{ color: "red" }}>{errors.makerComment?.message}</FormHelperText>
<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
<HStack mt={2} w={"100%"} justifyContent={"flex-end"}>
<Button
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
@@ -305,7 +426,13 @@ const CreateRequest = () => {
</HStack>
</Box>
<SelectInvestorModal setId={setId} setValue={setValue} onClose={onClose} isOpen={isOpen} onOpen={onOpen}/>
<SelectInvestorModal
setId={setId}
setValue={setValue}
onClose={onClose}
isOpen={isOpen}
onOpen={onOpen}
/>
</Box>
);
};

View File

@@ -138,7 +138,7 @@ import RequestRejectModal from "./RequestRejectModal";
"Deposit Date",
"Deposit Amount (BHD)",
"Support Image",
"Action",
"Action",
];

View File

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

View File

@@ -56,7 +56,7 @@ const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, da
const [updateVideoArtifacts] = useUpdateVideoArtifactsMutation()
// const {
// data
// } = useGetArtifactsQuery(id)
// } = useGetArtifactsQuery(id)
const {
control,
@@ -90,12 +90,12 @@ const AddCashDetails = ({ isOpen, onClose, firstField, actionId, setActionId, da
toast({
render: () => <ToastBox message={res?.error?.data?.message } status={"error"} />,
});
setAlert(false);
}
} catch (error) {
console.log(error);
setIsLoading(false);
}
};

View File

@@ -8,8 +8,8 @@ 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";
@@ -17,6 +17,9 @@ 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;
@@ -75,6 +78,11 @@ const CreateIO = () => {
Content: Destribution,
isDisabled: id ? true : true,
},
{
label: "IO Transaction",
Content: IOTransaction,
isDisabled: id ? true : true,
},
];
const [tabs, setTabs] = useState(initialTabsState);
@@ -114,7 +122,8 @@ const CreateIO = () => {
<Tab
isDisabled={isDisabled}
key={index}
fontSize={"sm"}
fontSize={"xs"}
fontWeight={500}
_selected={{
color: "#004118",
borderBottom: "2px solid #38a169",

View File

@@ -1,37 +1,49 @@
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 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("");
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",
});
};
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(() => {
@@ -42,32 +54,38 @@ const Destribution = () => {
return () => clearTimeout(timer);
}, []);
// Table setup
const tableHeadRow = [
// "Sr.No",
"Date",
"Amount",
"% of Investment"
];
// 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));
// 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) => ({
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": (
"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"}
@@ -78,7 +96,7 @@ const Destribution = () => {
{formatDate(item.transactionDate)}
</Text>
),
"Amount": (
Amount: (
<Text
justifyContent={"center"}
as={"span"}
@@ -86,11 +104,13 @@ const Destribution = () => {
fontWeight={"500"}
className="d-flex align-items-center web-text-small"
>
<Badge ms={1} colorScheme="green" me={1}>$</Badge>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{`${parseFloat(item.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text>
),
"% of Investment": (
@@ -106,12 +126,8 @@ const Destribution = () => {
),
}));
const handleDelete = () => {
const updatedNav = navDetails.filter(
(sponsor) => sponsor.id !== actionId
);
const updatedNav = navDetails.filter((sponsor) => sponsor.id !== actionId);
setTimeout(() => {
setNavDetails(updatedNav);
@@ -121,12 +137,11 @@ const Destribution = () => {
setIsLoading(true);
};
const Total = () => {
return (
<Table size="sm">
<Tbody>
<Tr backgroundColor="gray.50">
<Tr backgroundColor="gray.50">
<Th
textAlign={"center"}
p={3}
@@ -147,8 +162,13 @@ const Destribution = () => {
wordBreak="normal"
overflowWrap="normal"
>
<Badge ms={1} colorScheme="green" me={1}>$</Badge>{IODetails?.total_distributeToInvestor_amt?.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{IODetails?.total_distributeToInvestor_amt?.toLocaleString(
undefined,
{ minimumFractionDigits: 2, maximumFractionDigits: 2 }
)}
</Th>
<Th
textAlign={"center"}
@@ -167,62 +187,52 @@ const Destribution = () => {
);
};
return (
<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</HStack>
</Box>
<NormalTable
centered={true}
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
total={<Total />}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
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>
<AddIONav isOpen={isOpen} onClose={onClose} firstField={firstField} />
</Box>
);
};
<NormalTable
centered={true}
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
total={<Total/>}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<AddIONav
isOpen={isOpen}
onClose={onClose}
firstField={firstField} />
</Box>
)
}
export default Destribution
export default Destribution;

View File

@@ -116,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) {
@@ -134,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" />,
});
}
@@ -372,7 +372,7 @@ const sortedDataVideo = [...(IObyID?.data?.artifactsVideo || [])]?.sort(
Manage IO Images
</Box>
<HStack>
{IObyID?.data?.artifactsImage?.length !== 0 &&<SetDisplayOrderIOArtifactsImages data={sortedDataImage} />}
<Button

View File

@@ -10,6 +10,7 @@ import {
DrawerOverlay,
FormControl,
FormErrorMessage,
FormHelperText,
FormLabel,
Input,
Stack,
@@ -26,7 +27,8 @@ 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"),
@@ -152,21 +154,25 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, da
<DrawerBody>
<Stack spacing={4}>
<FormControl isInvalid={errors.artifactName} isRequired={true}>
<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} isRequired={true}>
<FormControl isInvalid={errors.artifactStreamingURL} isRequired>
<FormLabel fontSize={"sm"}>Artifact Streaming URL</FormLabel>
<Controller
name="artifactStreamingURL"
@@ -211,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}
/>
</>

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,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;

View File

@@ -0,0 +1,466 @@
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 } 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",
...(localStorage?.getItem('role')!==encryptString(import.meta.env.VITE_VITE_MAKER) ? ["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"}>
{localStorage?.getItem("role") !== encryptString(import.meta.env.VITE_VITE_MAKER) ? <Box>
{index===0&&<Box display={"flex"} justifyContent={"center"} gap={2}>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="Approve"
bg="#fff"
color={"green.500"}
placement="left-start"
>
<Button
// colorScheme="forestGreen"
// color="green.500"
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={2}
py={1}
onClick={() => {
setActionId(item.id);
onConfirmOpen();
}}
colorScheme="green"
variant={"solid"}
cursor={"pointer"}
>
<CheckIcon fontSize={"12px"} />
</Button>
</Tooltip>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="Reject"
bg="#fff"
color={"red.500"}
placement="left-start"
>
<Button
colorScheme="red"
// color="red.500"
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={2}
onClick={() => {
setActionId(item.id);
onRejectOpen();
}}
py={1}
// variant={"solid"}
>
<CloseIcon fontSize={"10px"} />
</Button>
</Tooltip></Box>}
</Box> : <Button
colorScheme="green"
rounded={"sm"}
size={"xs"}
px={2}
py={1}
fontWeight={500}
onClick={() => {
setActionId(item.id);
}}
>
<ViewIcon me={"4px"} /> View
</Button>}
</Box>
),
}));
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setCaseDetails(updatedSponsors);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const ioCashExporteDetails = IODetails?.ioCashStatusHistory?.Approved?.map(
(item, index) => ({
"Transaction date": item?.transactionDate,
"Transaction type": item?.transactionType,
Amount: parseFloat(item?.transactionAmount) || 0,
Comments: item?.comments,
})
);
const Total = () => {
return (
<Table size="sm">
<Tbody backgroundColor="gray.50">
<Tr>
<Th
textAlign={"center"}
p={3}
width="130px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
Balance in IO Cash
</Th>
<Th
textAlign={"center"}
p={3}
width="150px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{" "}
</Th>
<Th
textAlign={"center"}
p={3}
width="150px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{}
</Th>
<Th
textAlign={"center"}
p={3}
width="100px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(IODetails?.ioCash || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
</Th>
<Th
textAlign={"center"}
p={3}
width="100px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
>
{" "}
</Th>
<Th
textAlign={"center"}
p={3}
width="100px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
></Th>
<Th
textAlign={"center"}
p={3}
width="80px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
></Th>
<Th
textAlign={"center"}
p={3}
width="50px"
color={"#004118"}
whiteSpace="normal"
wordBreak="normal"
overflowWrap="normal"
></Th>
</Tr>
</Tbody>
</Table>
);
};
const handleAdd = async () => {
try {
const res = await updateIOCase(id);
if (res?.data) {
// toast({
// render: () => (
// <ToastBox status={"success"} message={res?.data?.message} />
// ),
// });
setIsLoading(false);
onOpen();
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsLoading(false);
}
} catch (error) {}
};
return (
<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
{/* <HStack display={"flex"} alignItems={"center"}>
{IODetails?.isInvestedAmount ? (
localStorage?.getItem('role') ==="Maker"&& <Button
onClick={handleAdd}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add
</Button>
) : null}
</HStack> */}
</HStack>
</Box>
<NormalTable
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
// total={<Total />}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<AddCaseDetails
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
<RequestApproveModal
// data={data?.data?.rows}
isOpen={isConfirmOpen}
onClose={onConfirmClose}
id={actionId}
// firstField={firstField}
/>
<RequestRejectModal
isOpen={isRejectOpen}
onClose={onRejectClose}
id={actionId}
/>
</Box>
);
};
export default Pending;

View File

@@ -0,0 +1,357 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Table,
Tag,
Tbody,
Text,
Th,
Tooltip,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react";
import { AddIcon} from "@chakra-ui/icons";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import AddCashDetails from "../AddCashDetails";
import AddRejected from "./AddRejected";
import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
import { useParams } from "react-router-dom";
import ToastBox from "../../../../Components/ToastBox";
import AddCaseDetails from "./AddCaseDetails";
const formatDate = (date) => new Date(date).toLocaleDateString();
const Rejected = () => {
const params = useParams()
const id = params?.id
const toast = useToast();
const firstField = useRef();
const { isOpen, onOpen, onClose } = useDisclosure();
const { IODetails, approved, setApproved } =
useContext(GlobalStateContext);
const [searchTerm, setSearchTerm] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [deleteAlert, setDeleteAlert] = useState(false);
const [actionId, setActionId] = useState(false);
const [mouseEntered, setMouseEntered] = useState(false);
const [mouseEnteredId, setMouseEnteredId] = useState("");
const [updateIOCase] = useUpdateIOCaseMutation()
useEffect(() => {
// Simulate loading
const timer = setTimeout(() => {
setIsLoading(false);
}, 1500);
// Cleanup the timer on component unmount
return () => clearTimeout(timer);
}, []);
const formatDate = (date) => {
return new Date(date).toLocaleDateString("en-GB", {
day: "2-digit",
month: "2-digit",
year: "numeric",
});
};
// Table filter
const filteredData = IODetails?.ioCashStatusHistory?.Reject?.filter((item) => {
// Filter by name (case insensitive)
const name = item.transactionDate;
const searchLower = searchTerm.toLowerCase();
const nameMatches = name.toLowerCase().includes(searchLower);
return nameMatches;
});
const tableHeadRow = [
"Sr No.",
"Transaction Date",
"Transaction Type",
"Amount",
"Comments",
"Update By",
"Update On",
];
const extractedArray = filteredData?.map((item, index) => ({
id: item?.id,
"Sr No.": (
<Text
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
{index + 1}.
</Text>
),
"Transaction Date": (
<Text
as={"span"}
color={"gray.600"}
fontWeight={"500"}
>
{formatDate(item?.transactionDate)}
</Text>
),
"Transaction Type": (
<Text as={"span"} color={"gray.600"} fontWeight={"500"}>
{item?.transactionType}
</Text>
),
"Amount": (
<Text
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
<Badge ms={1} colorScheme="green" me={1}>
$
</Badge>
{parseFloat(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;

View File

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

View File

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

View File

@@ -29,7 +29,7 @@ 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();
@@ -75,7 +75,7 @@ const IOCashDetails = () => {
"Update by ",
"Update On",
];
const handleUpdateStatus = debounce((id) => {
setCaseDetails((prevSponser) =>
prevSponser.map((sponsor) =>
@@ -134,9 +134,9 @@ const IOCashDetails = () => {
</Badge>
{/* {parseFloat(item.transactionAmount || 0).toLocaleString()} */}
{`${parseFloat(item.transactionAmount || 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`}
</Text>
),
Comments: (
@@ -180,8 +180,6 @@ const IOCashDetails = () => {
),
}));
const customHeaders = [
{ label: "Date", key: "transactionDate" },
{ label: "Transaction type", key: "transactionType" },
@@ -192,12 +190,12 @@ const IOCashDetails = () => {
// Add more headers as needed
];
const ioCashExporteDetails = IODetails?.ioCashHistory?.map((item, index) =>({
"Date": item?.transactionDate,
const ioCashExporteDetails = IODetails?.ioCashHistory?.map((item, index) => ({
Date: item?.transactionDate,
"Transaction type": item?.transactionType,
"Amount":parseFloat(item?.transactionAmount) || 0,
"Comments": item?.comments
}))
Amount: parseFloat(item?.transactionAmount) || 0,
Comments: item?.comments,
}));
console.log(ioCashExporteDetails);
@@ -312,8 +310,7 @@ const IOCashDetails = () => {
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
<HStack display={"flex"} alignItems={"center"}>
<Button
onClick={() =>
exportToExcelNew(ioCashExporteDetails, "IO Cash History")
@@ -324,25 +321,24 @@ const IOCashDetails = () => {
variant={"outline"}
rounded={"sm"}
fontSize={"xs"}
isDisabled={ioCashExporteDetails?.length === 0}
>
Export xls
</Button>
{IODetails?.isInvestedAmount ? (
<Button
onClick={onOpen}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add IO Cash
</Button>
) : null}
</HStack>
{IODetails?.isInvestedAmount ? (
<Button
onClick={onOpen}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add IO Cash
</Button>
) : null}
</HStack>
</HStack>
</Box>
@@ -376,4 +372,4 @@ const IOCashDetails = () => {
);
};
export default IOCashDetails;
export default IOCashDetailsOld;

View File

@@ -235,7 +235,6 @@ const IODetails = ({ enableNextTab, index, data }) => {
const [values, setValues] = useState(id?minInvestmentById:miniValue);
console.log(values);
const formatNumber = (num) => {
// Remove non-numeric characters and format with commas

View File

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

View File

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

View File

@@ -0,0 +1,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;

View File

@@ -0,0 +1,339 @@
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 } 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",
...(localStorage?.getItem("role") !== encryptString(import.meta.env.VITE_VITE_MAKER) ? ["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: (
<Box display={"flex"} justifyContent={"center"}>
<Box>
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="Approve"
bg="#fff"
color={"green.500"}
placement="left-start"
>
<Button
// colorScheme="forestGreen"
// color="green.500"
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={2}
py={1}
onClick={() => {
setActionId(item.id);
onConfirmOpen();
}}
colorScheme="green"
variant={"solid"}
cursor={"pointer"}
>
<CheckIcon fontSize={"12px"} />
</Button>
</Tooltip>
<Tooltip
rounded={"sm"}
fontSize={"xs"}
label="Reject"
bg="#fff"
color={"red.500"}
placement="left-start"
>
<Button
colorScheme="red"
// color="red.500"
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={2}
onClick={() => {
setActionId(item.id);
onRejectOpen();
}}
py={1}
// variant={"solid"}
>
<CloseIcon fontSize={"10px"} />
</Button>
</Tooltip>
</Box>
</Box>
</Box>
),
}));
const handleDelete = () => {
const updatedSponsors = sponser.filter(
(sponsor) => sponsor.id !== actionId
);
setTimeout(() => {
setCaseDetails(updatedSponsors);
setDeleteAlert(false);
setIsLoading(false);
}, 100);
setIsLoading(true);
};
const handleAdd = async () => {
try {
const res = await updateIOCase(id);
if (res?.data) {
// toast({
// render: () => (
// <ToastBox status={"success"} message={res?.data?.message} />
// ),
// });
setIsLoading(false);
onOpen();
} else if (res?.error) {
toast({
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
setIsLoading(false);
}
} catch (error) {}
};
return (
<Box {...OPACITY_ON_LOAD} pb={0}>
<Box bg="white.500">
<HStack
display={"flex"}
justifyContent={"space-between"}
pb={3}
spacing="24px"
>
<Input
type="search"
width={300}
placeholder="Search..."
size="sm"
rounded="sm"
focusBorderColor="green.500"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
{/* {IODetails?.isInvestedAmount
? localStorage?.getItem("role") === "Maker" && (
<Button
onClick={handleAdd}
leftIcon={<AddIcon />}
colorScheme="forestGreen"
size={"sm"}
rounded={"sm"}
fontSize={"xs"}
>
Add
</Button>
)
: null} */}
</HStack>
</Box>
<NormalTable
emptyMessage={`We don't have any Sponers`}
tableHeadRow={tableHeadRow}
data={extractedArray}
isLoading={isLoading}
viewActionId={actionId}
setViewActionId={setActionId}
setMouseEnteredId={setMouseEnteredId}
setMouseEntered={setMouseEntered}
/>
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>
<AddNavDetails
isOpen={isOpen}
onClose={onClose}
firstField={firstField}
/>
<RequestApproveModal
// data={data?.data?.rows}
isOpen={isConfirmOpen}
onClose={onConfirmClose}
id={actionId}
// firstField={firstField}
/>
<RequestRejectModal
isOpen={isRejectOpen}
onClose={onRejectClose}
id={actionId}
/>
</Box>
);
};
export default Pending;

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,177 @@
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import 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;

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

View File

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

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

View File

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

View File

@@ -0,0 +1,298 @@
import {
Avatar,
Badge,
Box,
Button,
HStack,
Input,
Table,
Tag,
Tbody,
Text,
Th,
Tooltip,
Tr,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useRef, useState } from "react";
import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import { CheckIcon, CloseIcon, ViewIcon } from "@chakra-ui/icons";
import RequestApproveModal from "./RequestApproveModal";
import RequestRejectModal from "./RequestRejectModal";
import ViewAmountInvested from "./ViewAmountInvested";
import ViewDistributionInvestor from "./ViewDistributionInvestor";
import ViewExit from "./ViewExit";
import ViewCancel from "./ViewCancel";
import { encryptString, isMaker } from "../../../../Constants/Constants";
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 {
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",
});
};
console.log(
"==============panding",
IODetails?.ioTransactionRecords?.Pending
);
// 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();
} else if (item?.transactionType === "Exit") {
onExitOpen();
} else if (item?.transactionType === "Cancel") {
onCancelOpen();
}
}}
>
{isMaker() ? <ViewIcon me={"4px"} /> : null}{" "}
{localStorage?.getItem("role") ===
encryptString(import.meta.env.VITE_VITE_MAKER)
? "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}
/>
<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;

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

View File

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

View File

@@ -0,0 +1,176 @@
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ToastBox from "../../../../Components/ToastBox";
import { 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;

View File

@@ -0,0 +1,176 @@
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
Textarea,
useDisclosure,
useToast,
} from "@chakra-ui/react";
import React, { useEffect, useState } from "react";
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers/yup";
import { useForm } from "react-hook-form";
import ToastBox from "../../../../Components/ToastBox";
import { 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;

View File

@@ -0,0 +1,294 @@
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 } 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>
{localStorage?.getItem("role") !== encryptString(import.meta.env.VITE_VITE_MAKER) && <ModalFooter>
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onConfirmOpen();
}}
colorScheme="forestGreen"
variant={"solid"}
cursor={"pointer"}
>
Approve
</Button>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onRejectOpen();
}}
>
Reject
</Button>
</Box>
</ModalFooter>}
</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;

View File

@@ -0,0 +1,368 @@
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 } 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>
{localStorage?.getItem("role") !== encryptString(import.meta.env.VITE_VITE_MAKER) && <ModalFooter pt={0}>
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onConfirmOpen();
}}
colorScheme="forestGreen"
variant={"solid"}
cursor={"pointer"}
>
Approve
</Button>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onRejectOpen();
}}
>
Reject
</Button>
</Box>
</ModalFooter>}
</ModalContent>
<ApprovedCancelTransaction
isOpen={isConfirmOpen}
onClose={onConfirmClose}
onBigModalClose={onClose}
id={cancleId}
/>
<RequestRejectModal
isOpen={isRejectOpen}
onClose={onRejectClose}
onBigModalClose={onClose}
id={cancleId}
/>
</Modal>
);
};
export default ViewCancel;

View File

@@ -0,0 +1,274 @@
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 } from "../../../../Constants/Constants";
const ViewDistributionInvestor = ({ isOpen, onClose,id:exitId }) => {
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(() => {
console.log("hiit useEffectc");
// handleCalculate(id, {
// amount: IODetails?.ioMVNAV,
// });
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(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"] : []), */}
{localStorage?.getItem("role") !== encryptString(import.meta.env.VITE_VITE_MAKER) &&<ModalFooter pt={0}>
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onConfirmOpen();
}}
colorScheme="forestGreen"
variant={"solid"}
cursor={"pointer"}
>
Approve
</Button>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onRejectOpen();
}}
>
Reject
</Button>
</Box>
</ModalFooter>}
</ModalContent>
<ApproveDistrubationModal
isOpen={isConfirmOpen}
onClose={onConfirmClose}
onBigModalClose={handleClose}
id={exitId}
/>
<RequestRejectModal
isOpen={isRejectOpen}
onClose={onRejectClose}
onBigModalClose={handleClose}
id={exitId}
/>
</Modal>
);
};
export default ViewDistributionInvestor;

View File

@@ -0,0 +1,330 @@
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 } 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>
{localStorage?.getItem("role") !== encryptString(import.meta.env.VITE_VITE_MAKER) && <ModalFooter pt={0}>
<Box display={"flex"} justifyContent={"center"} gap={2}>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onConfirmOpen();
}}
colorScheme="forestGreen"
variant={"solid"}
cursor={"pointer"}
>
Approve
</Button>
<Button
rounded={"sm"}
size={"xs"}
textTransform={"inherit"}
fontWeight={500}
px={3}
py={2}
onClick={() => {
setActionId(id); // Use the `id` variable from params
onRejectOpen();
}}
>
Reject
</Button>
</Box>
</ModalFooter>}
</ModalContent>
<ApprovedExit
isOpen={isConfirmOpen}
onClose={onConfirmClose}
onBigModalClose={onClose}
id={investerId}
/>
<RequestRejectModal
isOpen={isRejectOpen}
onClose={onRejectClose}
onBigModalClose={onClose}
id={investerId}
/>
</Modal>
);
};
export default ViewExit;

View File

@@ -363,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}
/>

View File

@@ -100,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) => {
@@ -164,7 +164,7 @@ const Investors = ({ data }) => {
"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
"Total Return on Investment": parseFloat(item?.Total_Return_On_Investment) || 0, // Convert to float
}));
console.log(exportInvestorDetails);
@@ -182,7 +182,7 @@ const Investors = ({ data }) => {
{item?.clientReference_id}
</Text>
),
"First name": (
"First Name": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
@@ -193,7 +193,7 @@ const Investors = ({ data }) => {
{item.firstName}
</Text>
),
"Last name": (
"Last Name": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
@@ -204,7 +204,7 @@ const Investors = ({ data }) => {
{item.lastName}
</Text>
),
"Investment amount": (
"Investment Amount": (
<Text
justifyContent={slideFromRight ? "right" : "left"}
as={"span"}
@@ -313,7 +313,7 @@ const Investors = ({ data }) => {
})}`}
</Text>
),
"Total return on Investment": (
"Total Return on Investment": (
<Text
justifyContent={slideFromRight ? "right" : "center"}
as={"span"}
@@ -508,7 +508,7 @@ console.log(IODetails?.investors);
>
Export xls
</Button>
<Box as="span">
{/* <Box as="span">
<Icon
ms={0}
animation={
@@ -524,7 +524,7 @@ console.log(IODetails?.investors);
_hover={{ bg: "gray.100" }}
cursor={"pointer"}
/>
</Box>
</Box> */}
</Box>
<HStack

View File

@@ -89,7 +89,7 @@ const KeyMerits = ({ enableNextTab, index, data: prepopData }) => {
const res = await deleteKeyMerits(actionId);
if (res?.data?.statusCode === 200) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
render: () => <ToastBox message={res?.data?.message} status={"success"} />,
});
setIsBtnLoading(false);
setDeleteAlert(false);

View File

@@ -297,7 +297,7 @@ const InvestmentEdit = ({ isOpen, onClose, thirdField, id, data }) => {
onClose={() => setAlert(false)}
alertHandler={handleConfirm}
isLoading={isLoading}
message="Are you sure you want to add this document?"
message="Are you sure you want to add this Investment?"
/>
</Drawer>
);

View File

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

View File

@@ -10,6 +10,7 @@ import {
DrawerOverlay,
FormControl,
FormErrorMessage,
FormHelperText,
FormLabel,
Icon,
Image,
@@ -35,7 +36,7 @@ import { IoMdRemoveCircleOutline } from "react-icons/io";
const investmentImageSchema = yup.object().shape({
artifactName: yup.string().required("Artifact image name is required")
.max(25, "File name must be at most 25 characters"),
.max(50, "Approve Comment cannot be more than 50 characters"),
artifactPathName: yup.mixed().required("Artifact image is required"),
});
@@ -226,19 +227,24 @@ const IOArtifactsAdd = ({
<Controller
name="artifactName"
control={control}
// maxLength={50}
render={({ field }) => (
<Input
{...field}
fontSize={"sm"}
type="text"
size={"sm"}
maxLength={25}
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
@@ -354,7 +360,7 @@ const IOArtifactsAdd = ({
isOpen={alert}
onClose={handleAlertClose}
alertHandler={handleSave}
message={"Are you sure you want to add this artifact?"}
message={"Are you sure you want to update this artifact?"}
isLoading={loading}
/>
</>

View File

@@ -60,7 +60,7 @@ export const investmentDocSchema = yup.object().shape({
// return value && value.size <= 2 * 1024 * 1024; // 2MB in bytes
// })
fileName: yup.string().required("File name is required")
.max(25, "File name must be at most 25 characters"), // Maximum length validation,
.max(30, "File name must be at most 30 characters"), // Maximum length validation,
documentNameArabic: yup.string().required("File name Arabic is required")
.max(25, "File name must be at most 30 characters"),
});
@@ -91,6 +91,7 @@ const InvestmentDocuments = ({
const {
register,
handleSubmit,
watch,
reset,
formState: { errors },
} = useForm({
@@ -210,13 +211,17 @@ const InvestmentDocuments = ({
fontSize="sm"
type="text"
size="sm"
maxLength={25} // Maximum length constraint in the input field
maxLength={30} // Maximum length constraint in the input field
/>
{errors.fileName && (
<Text mt={1} fontSize="xs" fontWeight={500} color="red">
{errors.fileName.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 30 characters. You have entered </Box>
{watch("fileName")?.length || 0} characters.
</FormHelperText>
</FormControl>
@@ -229,13 +234,17 @@ const InvestmentDocuments = ({
type="text"
size="sm"
textAlign={'right'}
maxLength={30} // Maximum length constraint in the input field
maxLength={35} // Maximum length constraint in the input field
/>
{errors.documentNameArabic && (
<Text mt={1} fontSize="xs" fontWeight={500} color="red">
{errors.documentNameArabic.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Box as="span" me={1}>Maximum length should be 35 characters. You have entered </Box>
{watch("documentNameArabic")?.length || 0} characters.
</FormHelperText>
</FormControl>

View File

@@ -360,7 +360,7 @@ const KeyMeritsAdd = ({ isOpen, onClose, firstField, id, icons }) => {
onClose={handleClose}
// alertHandler={handleSave}
alertHandler={handleConfirm}
message={"Are you sure you want to add this key merit?"}
message={"Are you sure you want to add this key merits?"}
isLoading={isLoading}
/>
</>

View File

@@ -367,7 +367,7 @@ const KeyMeritsEdit = ({
isOpen={alert}
onClose={() => setAlert(false)}
alertHandler={handleSave}
message={"Are you sure you want to add this key merit?"}
message={"Are you sure you want to update this key merits?"}
isLoading={isLoading}
/>
</>

View File

@@ -46,7 +46,7 @@ const AmountInvested = ({ isOpen, onClose }) => {
const toast = useToast();
const id = params?.id;
const {
control,
control,
register,
handleSubmit,
reset,
@@ -114,7 +114,7 @@ const AmountInvested = ({ isOpen, onClose }) => {
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalOverlay />
<ModalContent>
<ModalHeader fontSize={"md"}>Amount Invested</ModalHeader>
<ModalCloseButton />

View File

@@ -28,8 +28,10 @@ import NormalData from "../../../../Components/DataTable/NormalTable";
import { useContext, useState } from "react";
import { AddIcon } from "@chakra-ui/icons";
import {
useAddIOTransactionMutation,
useGetDistributedToInvestorMutation,
useGetDistributionInvestorMutation,
useSaveIOTransactionMutation,
useUpdateExitToInvestorMutation,
} from "../../../../Services/io.service";
import { useParams } from "react-router-dom";
@@ -42,6 +44,7 @@ import CurrencyInput from "../../../../Components/CurrencyInput";
import { IoCalculator } from "react-icons/io5";
import { debounce } from "../../../Master/Sponser/AddSponser";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import { validate } from "uuid";
@@ -74,11 +77,6 @@ const DistributionInvestor = ({ isOpen, onClose }) => {
amount: yup
.string()
.required("Amount is required")
.test(
"is-positive",
"Amount should be greater than zero",
(value) => parseFloat(value) > 0 // Check if the amount is greater than zero
)
.test(
"max",
`Distribution amount should not be greater than IO cash amount ${IODetails?.ioCash}`,
@@ -91,7 +89,6 @@ const DistributionInvestor = ({ isOpen, onClose }) => {
}
),
});
const investor = yup.object().shape({
amount: yup.string().required("Amount is required"),
@@ -107,6 +104,8 @@ const DistributionInvestor = ({ isOpen, onClose }) => {
resolver: yupResolver(investorExit),
});
const [saveIOTransaction] = useSaveIOTransactionMutation();
// ====================================================[Table Setup]================================================================
@@ -320,45 +319,81 @@ const DistributionInvestor = ({ isOpen, onClose }) => {
} catch (error) {}
};
// const onFinalSubmit = async (data) => {
// setIsFinalCalculateLoading(true);
// if (!isCalcualtedData) {
// setIsFinalCalculateLoading(false);
// return toast({
// render: () => (
// <ToastBox
// message={"Please calculate investment first."}
// status="warn"
// />
// ),
// });
// }
// const finalData = {
// transactionAmount: data?.amount,
// };
// try {
// const res = await getFinalDistributionInvestment({ id, data: finalData });
// console.log(finalData);
// if (res?.error?.status === 401) {
// toast({
// render: () => (
// <ToastBox message={res?.error?.data?.message} status="error" />
// ),
// });
// } else if (res?.data?.statusCode === 200) {
// toast({
// render: () => <ToastBox message={res?.data?.message} />,
// });
// handleClose();
// }
// } catch (error) {
// console.error("An error occurred:", error);
// } finally {
// handleClose();
// }
// };
const onFinalSubmit = async (data) => {
setIsFinalCalculateLoading(true);
if (!isCalcualtedData) {
setIsFinalCalculateLoading(false);
return toast({
render: () => (
<ToastBox
message={"Please calculate investment first."}
status="warn"
/>
),
});
}
const finalData = {
const currentDate = new Date();
const dataToSend = {
transactionDate: currentDate,
transactionAmount: data?.amount,
};
}
console.log("dataaaaaaa",dataToSend);
try {
const res = await getFinalDistributionInvestment({ id, data: finalData });
console.log(finalData);
if (res?.error?.status === 401) {
const res = await saveIOTransaction({ id,data: dataToSend });
console.log(res?.data?.data);
onClose();
if (!isCalcualtedData) {
setIsFinalCalculateLoading(false);
return toast({
render: () => (
<ToastBox
message={"Please calculate investment first."}
status="warn"
/>
),
});
} else if (res?.error) {
toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status="error" />
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
} else if (res?.data?.statusCode === 200) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
});
handleClose();
// setIsLoading(false);
}
} catch (error) {
console.error("An error occurred:", error);
} finally {
handleClose();
}
} catch (error) {}
};
const handleClose = () => {

View File

@@ -28,6 +28,7 @@ import NormalData from "../../../../Components/DataTable/NormalTable";
import { useContext, useState } from "react";
import { AddIcon } from "@chakra-ui/icons";
import {
useExitIOTransactionMutation,
useGetDistributedToInvestorMutation,
useGetDistributionInvestorMutation,
useUpdateExitToInvestorMutation,
@@ -94,7 +95,7 @@ const Exit = ({ isOpen, onClose }) => {
const res = await getDistributionInvestment({ id, data });
console.log(res?.data?.data);
if (res?.error?.status === 401) {
if (res?.error?.status === 401) {
// toast({
// render: () => (
// <ToastBox message={res?.error?.data?.message} status={"error"} />
@@ -122,7 +123,7 @@ const Exit = ({ isOpen, onClose }) => {
const [getDistributionInvestment] = useGetDistributionInvestorMutation();
const [getFinalDistributionInvestment] =
useGetDistributedToInvestorMutation();
const [updateExitToInvestor] = useUpdateExitToInvestorMutation();
const [exitIOTransaction] = useExitIOTransactionMutation();
const investor = yup.object().shape({
amount: yup.string().required("Amount is required"),
@@ -340,46 +341,69 @@ const Exit = ({ isOpen, onClose }) => {
} catch (error) {}
};
const onFinalSubmit = async (data) => {
console.log("hit");
setIsFinalCalculateLoading(true);
// if (!isCalcualtedData) {
// setIsFinalCalculateLoading(false);
// return toast({
// render: () => (
// <ToastBox
// message={"Please calculate investment first."}
// status="warn"
// />
// ),
// });
// }
// const onFinalSubmit = async (data) => {
// console.log("hit");
// setIsFinalCalculateLoading(true);
// const finalData = {
// transactionAmount: IODetails?.ioMVNAV,
// };
const finalData = {
// try {
// const res = await exitIOTransaction({ id, data: finalData });
// console.log(finalData);
// if (res?.error?.status === 401) {
// toast({
// render: () => (
// <ToastBox message={res?.error?.data?.message} status="error" />
// ),
// });
// } else if (res?.data?.statusCode === 200) {
// toast({
// render: () => <ToastBox message={res?.data?.message} />,
// });
// handleClose();
// }
// } catch (error) {
// console.error("An error occurred:", error);
// } finally {
// handleClose();
// }
// };
const onFinalSubmit = async () => {
setIsCalculateLoading(true);
const currentDate = new Date();
const dataToSend = {
transactionDate: currentDate,
transactionAmount: IODetails?.ioMVNAV,
};
}
try {
const res = await updateExitToInvestor({ id, data: finalData });
console.log(finalData);
const res = await exitIOTransaction({ id,data: dataToSend });
console.log(res?.data?.data);
if (res?.error?.status === 401) {
toast({
onClose();
if (!isCalcualtedData) {
setIsFinalCalculateLoading(false);
return toast({
render: () => (
<ToastBox message={res?.error?.data?.message} status="error" />
<ToastBox
message={"Please calculate investment first."}
status="warn"
/>
),
});
} else if (res?.data?.statusCode === 200) {
} else if (res?.error) {
toast({
render: () => <ToastBox message={res?.data?.message} />,
render: () => (
<ToastBox status={"error"} message={res?.error?.data?.message} />
),
});
handleClose();
setIsLoading(false);
}
} catch (error) {
console.error("An error occurred:", error);
} finally {
handleClose();
}
} catch (error) {}
};
const handleClose = () => {
@@ -389,7 +413,7 @@ const Exit = ({ isOpen, onClose }) => {
};
return (
<Modal size={"xl"} isOpen={isOpen} onClose={handleClose}>
<Modal size={"xl"} isOpen={isOpen} onClose={handleClose} >
<ModalOverlay />
<ModalContent maxW={1000}>
<ModalHeader fontSize={"md"}>Exit Transaction</ModalHeader>
@@ -435,7 +459,7 @@ const Exit = ({ isOpen, onClose }) => {
{/* ) } */}
</ModalBody>
<ModalFooter pt={0}>
{isCalcualtedData ? (
{/* {isCalcualtedData ? ( */}
<>
<Button
bg={"hsla(139, 100%, 14%, 1)"}
@@ -455,9 +479,9 @@ const Exit = ({ isOpen, onClose }) => {
Close
</Button>
</>
) : (
{/* ) : (
""
)}
)} */}
</ModalFooter>
</ModalContent>
</Modal>

View File

@@ -55,7 +55,7 @@ const UpdateIONav = ({ isOpen, onClose }) => {
const [createIoNav] = useCreateIoNavMutation()
const onSubmit = async (data) => {
setIsLoading(true);
try {

View File

@@ -58,7 +58,7 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => {
let res;
// If selectedItem is 'Cancelled', make the updateCancelStatus API call
if (selectedItem === "Cancelled") {
if (selectedItem === import.meta.env.VITE_STATUS_CANCELLED) {
res = await updateCancleStatus({
id
});
@@ -119,17 +119,17 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => {
mb={1.5}
textTransform={"none"}
colorScheme={
selectedItem === "Draft"
selectedItem === import.meta.env.VITE_STATUS_DRAFT
? "gray"
: selectedItem === "Processing"
: selectedItem === import.meta.env.VITE_STATUS_PROCESSING
? "yellow"
: selectedItem === "Open"
: selectedItem === import.meta.env.VITE_STATUS_OPEN
? "blue"
: selectedItem === "Closed"
: selectedItem === import.meta.env.VITE_STATUS_CLOSED
? "green"
: selectedItem === "Exited"
: selectedItem === import.meta.env.VITE_STATUS_EXITED
? "red"
: selectedItem === "Cancelled"
: selectedItem === import.meta.env.VITE_STATUS_CANCELLED
? "orange"
: "purple"
}
@@ -154,24 +154,24 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => {
<Badge
rounded={"full"}
pt={1.5}
pb={1.5}
pb={1.5}
ps={4}
pe={4}
mt={1.5}
mb={1.5}
textTransform={"none"}
colorScheme={
statusAdmin === "Draft"
statusAdmin === import.meta.env.VITE_STATUS_DRAFT
? "gray"
: statusAdmin === "Processing"
: statusAdmin === import.meta.env.VITE_STATUS_PROCESSING
? "yellow"
: statusAdmin === "Open"
: statusAdmin === import.meta.env.VITE_STATUS_OPEN
? "blue"
: statusAdmin === "Closed"
: statusAdmin === import.meta.env.VITE_STATUS_CLOSED
? "green"
: statusAdmin === "Exited"
: statusAdmin === import.meta.env.VITE_STATUS_EXITED
? "red"
: statusAdmin === "Cancelled"
: statusAdmin === import.meta.env.VITE_STATUS_CANCELLED
? "orange"
: "purple"
}

View File

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

View File

@@ -1,52 +1,61 @@
import {
Box,
Button,
Input,
keyframes,
Stack,
Tab,
TabList,
TabPanel,
TabPanels,
Tabs,
Text,
useDisclosure,
useDisclosure
} from "@chakra-ui/react";
import { useContext, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import GlobalStateContext from "../../../Contexts/GlobalStateContext";
import { useContext, useEffect, useState } from "react";
import FormInputView from "../../../Components/FormInputView";
import { useForm } from "react-hook-form"; // assuming react-hook-form is used
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import { ArrowBackIcon } from "@chakra-ui/icons";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import InvestmentDocument from "../CreateIO/InvestmentDocument";
import Investors from "../CreateIO/Investors";
import IOArtifacts from "../CreateIO/IOArtifacts";
import KeyMerits from "../CreateIO/KeyMerits";
import ViewIOdataHeader from "./ViewIOdataHeader";
import ViewIOdetails from "./ViewIOdetails";
import ViewIOdocs from "./ViewIOdocs";
import ViewKeyMerits from "./ViewKeyMerits";
import ViewIOartifacts from "./ViewIOartifacts";
import ViewInvestors from "./ViewInvestors";
import ViewIOcash from "./ViewIOcash";
import ViewIOnav from "./ViewIOnav";
import ViewDistribution from "./ViewDistribution";
import InvestmentDocument from "../CreateIO/InvestmentDocument";
import KeyMerits from "../CreateIO/KeyMerits";
import Investors from "../CreateIO/Investors";
import EditIO from "../EditIO/EditIO";
import IOArtifacts from "../CreateIO/IOArtifacts";
import IOCashDetails from "../CreateIO/IOCashDetails";
import IONAVDetails from "../CreateIO/IONAVDetails";
import { useGetIOprepopulateDataQuery } from "../../../Services/io.service";
import UnderConstruction from "../../UnderConstruction";
// import IOCashDetails from "../CreateIO/IOCashDetailsold";
// import IONAVDetails from "../CreateIO/IONAVDetailsOld";
import { GoDotFill } from "react-icons/go";
import {
useGetIOByIdQuery,
useGetIOprepopulateDataQuery,
} from "../../../Services/io.service";
import Destribution from "../CreateIO/Destribution";
import IOCashDetails from "../CreateIO/IOCashDetails/IOCashDetails";
import IONAVDetails from "../CreateIO/IONAVDetails/IONAVDetails";
import IOTransaction from "../CreateIO/IOTransaction/IOTransaction";
const rotate = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`;
const ViewIOdata = () => {
const params = useParams();
const id = params?.id;
const { data, error, isLoading } = useGetIOprepopulateDataQuery();
const { data, error, isLoading, refetch } = useGetIOprepopulateDataQuery();
const {
data: IObyID,
isLoading: IObyIDisLoading,
error: IObyIDerror,
refetch: IObyIDrefetch,
} = useGetIOByIdQuery(id, { skip: !id });
const { isOpen, onOpen, onClose } = useDisclosure();
const navigate = useNavigate();
const [isEditing, setIsEditing] = useState(false);
const [isRefetchLoading, setIsRefetchLoading] = useState(false);
const { IODetails, setIODetails } = useContext(GlobalStateContext);
console.log(IODetails?.isInvestedAmount);
const tabs = [
{ label: "IO Details", content: <ViewIOdetails data={data?.data} /> },
@@ -64,9 +73,22 @@ const ViewIOdata = () => {
label: "Distribution to Investors",
content: <Destribution data={data?.data} />,
},
{
label: "IO Transaction",
content: <IOTransaction data={data?.data} />,
},
// { label: "Distribution to Investors", content: <UnderConstruction h={'75vh'} /> },
];
const handleRefresh = async () => {
setIsRefetchLoading(true);
await IObyIDrefetch();
setIsRefetchLoading(false);
};
console.log(IODetails?.ioNAVHistory);
return (
<Box
{...OPACITY_ON_LOAD}
@@ -87,7 +109,7 @@ const ViewIOdata = () => {
<Tabs mt={4}>
<TabList justifyContent={"space-between"} pe={4} alignItems={"center"}>
<Box display={"flex"}>
<Box display={"flex"} position={"relative"} w={"100%"}>
{tabs.map(({ label }, index) => (
<Tab
px={3}
@@ -96,20 +118,86 @@ const ViewIOdata = () => {
index === 1 ||
index === 2 ||
index === 3 ||
index === 4
index === 4 ||
index === 8
? false
: !IODetails?.isInvestedAmount
}
// isDisabled={
// index === 0 ||
// index === 1 ||
// index === 2 ||
// index === 3 ||
// index === 4
// ? false
// : !IODetails?.isInvestedAmount
// }
key={index}
fontSize={"sm"}
fontSize={"xs"}
_selected={{
color: "#004118",
borderBottom: "2px solid #38a169",
}}
fontWeight={500}
position={"relative"}
>
{label}
{label}{" "}
{(index === 5 &&
IODetails?.ioCashStatusHistory?.Pending?.length !== 0) ||
(index === 6 &&
IODetails?.ioNAVStatusHistory?.Pending?.length !== 0) ||
(index === 8 &&
IODetails?.ioTransactionRecords?.Pending?.length !== 0) ? (
<Box
as="span"
right={0}
color={"forestGreen"}
top={1}
position={"absolute"}
>
<GoDotFill />
</Box>
) : (
""
)}
</Tab>
))}
{/* <Box as="span" position={"absolute"} right={2} bottom={1}>
<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> */}
<Stack
position={"absolute"}
right={1}
bottom={1}
direction="row"
spacing={4}
>
<Button
isLoading={isRefetchLoading}
loadingText="Refresh"
colorScheme="forestGreen"
variant="solid"
size={"xs"}
onClick={handleRefresh}
fontWeight={400}
>
Refresh
</Button>
</Stack>
</Box>
</TabList>
<TabPanels>

View File

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

View File

@@ -1,4 +1,4 @@
import { Box, Button } from "@chakra-ui/react";
import { Box, Button, Text } from "@chakra-ui/react";
import React, { useContext, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
@@ -240,11 +240,6 @@ const ViewIOdetails = () => {
width: "32.3%",
value: IObyID?.data?.isShariah,
},
{
label: "Investment Type",
placeHolder: "Select option",
@@ -351,19 +346,22 @@ const ViewIOdetails = () => {
return (
<Box position={"relative"}>
<Button
position={"absolute"}
top={"-62px"}
right={0}
onClick={() => navigate(`/create-io/${params?.id}`)}
ps={4}
pe={4}
colorScheme="forestGreen"
rounded={"sm"}
size={"xs"}
>
Edit IO
</Button>
<Box display={"flex"} justifyContent={"space-between"}>
<Text></Text>
<Button
// position={"absolute"}
// top={"-62px"}
right={0}
onClick={() => navigate(`/create-io/${params?.id}`)}
ps={4}
pe={4}
colorScheme="forestGreen"
rounded={"sm"}
size={"xs"}
>
Edit IO
</Button>
</Box>
<FormInputView groupedFields={groupedFields} />
</Box>
);

View File

@@ -6,8 +6,6 @@ import {
HStack,
Input,
Select,
Switch,
Tag,
Text,
Tooltip,
useDisclosure,
@@ -103,7 +101,6 @@ const InvestorDetails = () => {
// ====================================================[Table Setup]================================================================
const tableHeadRow = [
"Sr No",
"Client ID",
"First Name",
"Last Name",
@@ -223,7 +220,7 @@ const InvestorDetails = () => {
>
{/* {item.KYCStatus ? "Completed" : "Not complete"} */}
{item?.KYCStatus === true ? "Completed" : "Not Completed"}
{item?.KYCStatus === true ? "Completed" : "NotCompleted"}
</Text>
</Box>
),

View File

@@ -49,7 +49,7 @@ const Kyc = () => {
<HStack spacing={4} mb={4}>
<FormControl>
<FormLabel mb={1} fontSize={"sm"}>
House/Unit
Address line 1
</FormLabel>
<Input
bg={"#ccc3"}
@@ -62,7 +62,7 @@ const Kyc = () => {
</FormControl>
<FormControl>
<FormLabel mb={1} fontSize={"sm"}>
Road/Street
Address line 2
</FormLabel>
<Input
bg={"#ccc3"}

View File

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

View File

@@ -24,7 +24,7 @@ export const addInvestmentType = yup.object().shape({
.string()
.required("Investment type is required")
.max(50, "Investment name cannot be more than 50 characters"),
note: yup.string().optional().max(255, "Note cannot exceed 255 characters"),
// note: yup.string().optional().max(255, "Note cannot exceed 255 characters"),
investmentTypeNameArabic: yup
.string()
.required("Investment type in required"),
@@ -107,27 +107,23 @@ const AddInvestmentType = () => {
isActive: isSwitchOn,
};
await updateInvestmentType({ data: formData, id }).then((response) => {
if (response?.data) {
if (response?.data?.statusCode === 200) {
toast({
render: () => (
<ToastBox
status={"success"}
message={response?.data?.message}
/>
),
render: () => <ToastBox message={response?.data?.message} />,
});
setIsLoadingBtn(false);
setAlert(false);
navigate("/investment-type");
} else if (response?.error) {
} else {
toast({
render: () => (
<ToastBox
status={"error"}
message={response?.error?.data?.message}
/>
<ToastBox message={"Something Went Wrong"} status={"error"} />
),
});
setIsLoadingBtn(false);
navigate("/investment-type");
}
});
} catch (error) {
@@ -143,50 +139,22 @@ const AddInvestmentType = () => {
};
await createInvestmentType(formData).then((response) => {
// if (response?.data?.statusCode === 201) {
// toast({
// render: () => <ToastBox message={response?.data?.message} />,
// });
// setIsLoadingBtn(false);
// navigate("/investment-type");
// } else {
// toast({
// render: () => (
// <ToastBox message={"Something Went Wrong"} status={"error"} />
// ),
// });
// setIsLoadingBtn(false);
// navigate("/investment-type");
// }
if (response?.data) {
if (response?.data?.statusCode === 201) {
toast({
render: () => (
<ToastBox
status={"success"}
message={response?.data?.message}
/>
),
render: () => <ToastBox message={response?.data?.message} />,
});
setAlert(false);
setIsLoadingBtn(false);
navigate("/investment-type");
} else if (response?.error) {
} else {
toast({
render: () => (
<ToastBox
status={"error"}
message={response?.error.data.message}
/>
<ToastBox message={"Something Went Wrong"} status={"error"} />
),
});
setAlert(false);
setIsLoadingBtn(false);
// navigate("/investment-type");
navigate("/investment-type");
}
});
} catch (error) {
@@ -231,7 +199,7 @@ const AddInvestmentType = () => {
placeHolder: " ",
name: "note",
type: "textarea",
// isRequired: true,
isRequired: false,
section: "",
maxLength: 255,
helperText: `Maximum length should be 255 characters. You have entered ${
@@ -243,7 +211,7 @@ const AddInvestmentType = () => {
placeHolder: " ",
name: "noteArabic",
type: "textarea",
// isRequired: true,
isRequired: false,
arabic: true,
section: "",
maxLength: 255,
@@ -286,7 +254,7 @@ const AddInvestmentType = () => {
placeHolder: " ",
name: "note",
type: "textarea",
// isRequired: true,
isRequired: false,
section: "",
maxLength: 255,
helperText: `Maximum length should be 255 characters. You have entered ${
@@ -298,7 +266,7 @@ const AddInvestmentType = () => {
placeHolder: " ",
name: "noteArabic",
type: "textarea",
// isRequired: true,
isRequired: false,
arabic: true,
section: "",
maxLength: 255,
@@ -346,22 +314,9 @@ const AddInvestmentType = () => {
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={14}>
{/* ===================== [Switch Button] ======================== */}
<Box
display={"flex"}
justifyContent={"space-between"}
alignItems={"center"}
mt={5}
px={4}
mb={5}
>
<Text
fontSize={"sm"}
mb={0}
onClick={() => navigate(-1)}
cursor={"pointer"}
>
<ArrowBackIcon fontSize={"xl"} me={2} />
Add Details
<Box display={"flex"} justifyContent={"space-between"} alignItems={"center"} mt={5} px={4} mb={5}>
<Text fontSize={"sm"} mb={0} onClick={() => navigate(-1)} cursor={"pointer"}>
<ArrowBackIcon fontSize={"xl"} me={2} />{params?.id ? "Edit Details" : "Add Details"}
</Text>
<SwitchButton isSwitchOn={isSwitchOn} setIsSwitchOn={setIsSwitchOn} />
</Box>
@@ -383,11 +338,7 @@ const AddInvestmentType = () => {
isOpen={alert}
onClose={() => setAlert(false)}
alertHandler={handleConfirm}
message={
id
? "Are you sure you want to update this document?"
: " Are you sure you want to add this document?"
}
message={id ? "Are you sure you want to update this Investment Type?" : " Are you sure you want to add this Investment Type?"}
isLoading={isLoadingBtn}
/>
</Box>

View File

@@ -232,16 +232,49 @@ const InvestmentType = () => {
// ==================== [Delete Function] =======================
// const handleDelete = async () => {
// console.log(actionId);
// setIsLoading(true);
// try {
// const response = await deleteInvestmentType(actionId);
// console.log(response);
// setIsLoading(false);
// setDeleteAlert(false);
// } catch (error) {}
// };
const handleDelete = async () => {
console.log(actionId);
setIsLoading(true);
try {
const response = await deleteInvestmentType(actionId);
console.log(response);
console.log(response?.data);
if (response?.error?.data?.code === 400) {
toast({
render: () => (
<ToastBox message={response?.error?.data?.message} status="error" />
),
});
} else if (
response?.data?.statusCode === 201 ||
response?.data?.statusCode === 200
) {
toast({
render: () => (
<ToastBox message={response?.data?.message} status="success" />
),
});
}
setIsLoading(false);
setDeleteAlert(false);
} catch (error) {}
} catch (error) {
console.error(error);
setIsLoading(false);
}
};
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
@@ -315,7 +348,7 @@ const InvestmentType = () => {
<CustomAlertDialog
onClose={() => setDeleteAlert(false)}
isOpen={deleteAlert}
message={"Are you sure you want to delete sponers?"}
message={"Are you sure you want to delete Investment Type?"}
alertHandler={handleDelete}
isLoading={isLoading}
/>

View File

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

View File

@@ -187,13 +187,13 @@ const Sponser = () => {
console.log(response?.data);
if(response?.error?.data?.code === 400){
toast({
render: () => <ToastBox message={response?.error?.data?.message} status={'warn'} />,
render: () => <ToastBox message={response?.error?.data?.message} status={'error'} />,
});
setIsLoading(false);
setDeleteAlert(false);
} else if(response?.data?.statusCode === 201 || response?.data?.statusCode === 200){
toast({
render: () => <ToastBox message={response?.data?.message} status={'error'} />,
render: () => <ToastBox message={response?.data?.message} status={'success'} />,
});
setIsLoading(false);
setDeleteAlert(false);
@@ -201,7 +201,7 @@ const Sponser = () => {
}
} catch (error) {}
};
console.log(isSponserLoading);

View File

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

View File

@@ -1,31 +1,31 @@
import { Box, Image, Spinner, Text } from '@chakra-ui/react'
import React from 'react'
import logo from '../assets/logo2.png'
import { Box, Image, Spinner, Text } from "@chakra-ui/react";
import React from "react";
import logo from "../assets/logo2.png";
const SplashScreen = () => {
return (
<Box
h={'100vh'}
display={'flex'}
justifyContent={'center'}
alignItems={'center'}
flexDirection={'column'}
gap={10}
h={"100vh"}
display={"flex"}
justifyContent={"center"}
alignItems={"center"}
flexDirection={"column"}
gap={10}
>
<Image src={logo} />
{/* <Spinner color='green.900' size='md' /> */}
<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>
<Image src={logo} />
<Spinner color='green.900' size='md' />
{/* <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> */}
</Box>
)
}
);
};
export default SplashScreen
export default SplashScreen;

View File

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

View File

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

View File

@@ -3,6 +3,7 @@ import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Heading,
HStack,
@@ -36,7 +37,8 @@ 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"),
});
const DrawalRequestApprove = ({
@@ -59,6 +61,7 @@ const DrawalRequestApprove = ({
const {
control,
register,
watch,
reset,
handleSubmit,
formState: { errors },
@@ -259,12 +262,17 @@ const DrawalRequestApprove = ({
size="sm"
placeholder={"Enter your comments...."}
resize={"none"}
maxLength={200}
/>
{errors.comment && (
<Text fontSize="xs" color="red">
{errors.comment.message}
</Text>
)}
<FormHelperText fontSize="xs" color="gray.500">
<Text as={"span"} me={1}>Maximum length should be 200 characters. You have entered</Text>
{watch("checkerComment")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>

View File

@@ -2,6 +2,7 @@ import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
@@ -24,7 +25,8 @@ import {
import { useDepositRejectMutation } from "../../../Services/drawal.request.service";
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 DrawalRequestReject = ({ isOpen, onClose, firstField ,id}) => {
@@ -34,6 +36,7 @@ import { useDepositRejectMutation } from "../../../Services/drawal.request.servi
const {
register,
watch,
reset,
handleSubmit,
formState: { errors },
@@ -101,16 +104,21 @@ import { useDepositRejectMutation } from "../../../Services/drawal.request.servi
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">
<Text as={"span"} me={1}>Maximum length should be 200 characters. You have entered</Text>
{watch("comments")?.length || 0} characters.
</FormHelperText>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
<Button
colorScheme="gray"
mr={3}
onClick={onClose}

View File

@@ -104,25 +104,23 @@ export const nav = [
title: "INVESTORS REQUEST",
type: "title",
},
{
title: "Fawateer Deposit",
submenu: [
{
title: "Aprover Request",
path: "/fawateer",
icon: RiMoneyDollarBoxLine,
},
{
title: "View History",
path: "/fawateer-history",
icon: RiExchangeBoxLine,
},
],
type: "accordion",
Icon: HiOutlineBanknotes,
}
,
{
title: "Fawateer Deposit",
submenu: [
{
title: "Aprover Request",
path: "/fawateer",
icon: RiMoneyDollarBoxLine,
},
{
title: "View History",
path: "/fawateer-history",
icon: RiExchangeBoxLine,
},
],
type: "accordion",
Icon: HiOutlineBanknotes,
},
{
title: "Bank Deposit",
submenu: [
@@ -233,6 +231,11 @@ export const nav = [
path: "/bank-details",
icon: RiBankLine,
},
{
title: "Sub Admin",
path: "/subadmin",
icon: RiFileUserLine,
},
],
type: "accordion",
Icon: MdOutlineAdminPanelSettings,

View File

@@ -46,6 +46,8 @@ import EmailNotification from "../Pages/EmailNotification/EmailNotification";
import User from "../Pages/User/User";
import AddUser from "../Pages/User/AddUser";
import Profile from "../Pages/Profile/Profile";
import SubAdmin from "../Pages/SubAdmin/SubAdmin";
import SubAdminUpdateCreate from "../Pages/SubAdmin/SubAdminUpdateCreate";
export const RouteLink = [
// =============[ Tanami ]================
@@ -123,6 +125,9 @@ export const RouteLink = [
// { path: "/bank-details", Component: UnderConstruction },
{ path: "/bank-details/edit-bank-details/:id", Component: EditBankDetails },
{ path: "/profile", Component: Profile },
{ path: "/subadmin", Component: SubAdmin },
{ path: "/subadmin/subadmin-update/:id", Component: SubAdminUpdateCreate },
{ path: "/subadmin/subadmin-update", Component: SubAdminUpdateCreate },
@@ -134,8 +139,5 @@ export const RouteLink = [
// { path: "/fawateer-approver", Component: ApproveRequest },
// { path: "/approver-history", Component: ApproveHistory },
];

View File

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

View File

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

View File

@@ -15,9 +15,9 @@ export const ioService = createApi({
"getArtifactsVideo",
"getInvestmentDocuments",
"getIOById",
"getSponser",
"getInvestmentType",
"getInvestmentTypeID"
"getSponser",
"getInvestmentType",
"getInvestmentTypeID",
],
endpoints: (builder) => ({
// =====[get prepopulate data]
@@ -28,7 +28,8 @@ export const ioService = createApi({
// =====[get]
getIOs: builder.query({
query: ({ page, size, ioStatus_xid, search }) => `/io/admin?page=${page}&size=${size}&ioStatus_xid=${ioStatus_xid}&search=${search}`,
query: ({ page, size, ioStatus_xid, search }) =>
`/io/admin?page=${page}&size=${size}&ioStatus_xid=${ioStatus_xid}&search=${search}`,
providesTags: ["getIO"],
}),
@@ -224,7 +225,7 @@ export const ioService = createApi({
invalidatesTags: ["getIOById"],
}),
// ======== [ Distribution Transaction ] ========
// ======== [ Distribution Transaction ] ========
getDistributionInvestor: builder.mutation({
query: ({ id, data }) => ({
@@ -261,9 +262,7 @@ export const ioService = createApi({
invalidatesTags: ["getIOById"],
}),
// ==============[ Displaye Orders ]===============
// ==============[ Displaye Orders ]===============
setDisplayOrder: builder.mutation({
query: ({ data }) => ({
@@ -309,7 +308,7 @@ export const ioService = createApi({
method: "POST",
body: data,
}),
invalidatesTags: ["getSponser","prePopulate"],
invalidatesTags: ["getSponser", "prePopulate"],
}),
// ========[Update Sponser]========
@@ -320,12 +319,11 @@ export const ioService = createApi({
method: "PATCH",
body: data,
}),
invalidatesTags: ["getSponser","prePopulate"],
invalidatesTags: ["getSponser", "prePopulate"],
}),
// ======[Get All]=====
getSponserMaster: builder.query({
query: ({ page, size, searchTerm }) => {
let baseURL = `/sponsor/admin/?search=${searchTerm || ""}`;
@@ -336,10 +334,10 @@ export const ioService = createApi({
},
providesTags: ["getSponser"],
}),
// ========[Delete Sponser]========
deleteSponser: builder.mutation({
deleteSponser: builder.mutation({
query: (id) => ({
url: `/sponsor/admin/delete/${id}`,
method: "DELETE",
@@ -356,7 +354,7 @@ export const ioService = createApi({
getSponserMasterActive: builder.query({
query: () => "/sponsor/admin/active",
}),
// ======[Get ID]=====
getSponserById: builder.query({
@@ -369,12 +367,8 @@ export const ioService = createApi({
query: () => `/sponsor/admin/active`,
}),
// ===============================[ Investment Type ]===================================
// ===============================[ Investment Type ]===================================
// ========[Get All]=========
getInvestmentTypes: builder.query({
@@ -405,11 +399,15 @@ export const ioService = createApi({
updateInvestmentType: builder.mutation({
query: ({ data, id }) => ({
url: `/investmentType/admin/${id}`,
url: `/investmentType/admin/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getInvestmentTypeID", "getInvestmentType", "prePopulate"],
invalidatesTags: [
"getInvestmentTypeID",
"getInvestmentType",
"prePopulate",
],
}),
// ========[Delete Investment]=======
@@ -419,23 +417,179 @@ export const ioService = createApi({
url: `/investmentType/admin/delete/${id}`,
method: "DELETE",
}),
invalidatesTags: ["getInvestmentType", 'prePopulate'],
invalidatesTags: ["getInvestmentType", "prePopulate"],
}),
profile: builder.query({
query: (id) => `/auth/admin/profile`,
query: () => `/auth/admin/profile`,
}),
// ========Add Io Details========
updateIOCase: builder.mutation({
query: (id) => ({
url: `/io/admin/maker-transaction/${id}/verify-pending-transaction-for-cash-and-nav`,
method: "POST",
}),
invalidatesTags: ["getIOById"],
}),
updateTransaction: builder.mutation({
query: (id) => ({
// url: `/io/admin/maker-transaction/${id}/verify-pending-transaction-for-cash-and-nav`,
url: `/io/admin/maker-transaction/${id}/verify-pending-transaction`,
method: "POST",
}),
invalidatesTags: ["getIOById"],
}),
addNavDetails: builder.mutation({
query: ({ id, data }) => ({
url: `/io/admin/maker-transaction/${id}/io-nav`,
method: "POST",
body: data,
}),
invalidatesTags: ["getIOById"],
}),
addIOTransaction: builder.mutation({
query: ({ id, data }) => ({
url: `/io/admin/maker-transaction/${id}/io-nav`,
method: "POST",
body: data,
}),
invalidatesTags: ["getIOById"],
}),
saveIOTransaction: builder.mutation({
query: ({ id, data }) => ({
url: `/io/admin/maker-transaction/${id}/io-yeild`,
method: "POST",
body: data,
}),
invalidatesTags: ["getIOById"],
}),
exitIOTransaction: builder.mutation({
query: ({ id, data }) => ({
url: `/io/admin/maker-transaction/${id}/io-exit`,
method: "POST",
body: data,
}),
invalidatesTags: ["getIOById"],
}),
addIoCase: builder.mutation({
query: (id) => ({
url: `/io/admin/maker-transaction/${id}/verify-pending-transaction`,
method: "POST",
}),
invalidatesTags: ["getIOById"],
}),
approveIOCase: builder.mutation({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/io-cash/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getIOById"],
}),
approveIONav: builder.mutation({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/io-nav/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getIOById"],
}),
approveDistribution: builder.mutation({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/distributed-to-investor/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getIOById"],
}),
approveExit: builder.mutation({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/exit/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getIOById"],
}),
approveInvested: builder.mutation({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/amount-invested/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getIOById"],
}),
approveDistributed: builder.mutation({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/distributed-to-investor/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getIOById"],
}),
approveExitTransaction: builder.mutation({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/exit/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getIOById"],
}),
approveCancleTransaction: builder.mutation({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/approved/cancel/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getIOById"],
}),
rejectIOCase: builder.mutation({
query: ({ id, data }) => ({
url: `/io/admin/checker-transaction/reject/${id}`,
method: "PATCH",
body: data,
}),
invalidatesTags: ["getIOById"],
}),
}),
@@ -470,7 +624,6 @@ export const {
useDeleteVideoArtifactsMutation,
useDeleteImageArtifactsMutation,
useSetDisplayOrderMutation,
useSetDisplayOrderIODocumentsMutation,
useSetDisplayOrderIOArtifactsImageMutation,
@@ -488,11 +641,9 @@ export const {
useGetDistributedToInvestorMutation,
useUpdateExitToInvestorMutation,
useUpdateCancleStatusMutation,
// ==============[ Sponser ]===============
// ==============[ Sponser ]===============
useGetSponserMasterQuery,
useGetSponserMasterActiveQuery,
useCreateSponserMutation,
@@ -501,7 +652,6 @@ export const {
useDeleteSponserMutation,
useGetActiveSponserMasterQuery,
// ============[ Investment Type ]========
useGetInvestmentTypesQuery,
@@ -509,6 +659,24 @@ export const {
useCreateInvestmentTypeMutation,
useUpdateInvestmentTypeMutation,
useDeleteInvestmentTypeMutation,
useProfileQuery
useProfileQuery,
// ========[ Add Io Details ]========
useUpdateIOCaseMutation,
useUpdateTransactionMutation,
useApproveIOCaseMutation,
useRejectIOCaseMutation,
useAddNavDetailsMutation,
useApproveIONavMutation,
useAddIOTransactionMutation,
useSaveIOTransactionMutation,
useApproveDistributionMutation,
useExitIOTransactionMutation,
useApproveExitMutation,
useApproveInvestedMutation,
useApproveDistributedMutation,
useApproveExitTransactionMutation,
useApproveCancleTransactionMutation
} = ioService;

View File

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

View File

@@ -1,131 +1,122 @@
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { encryptString } from '../Constants/Constants'
// Define a base query function with RTK Query
// export const baseQuery = fetchBaseQuery({
// baseUrl: 'https://sprint4.tanami.betadelivery.com/api/v1',
// prepareHeaders: (headers) => {
// const token = localStorage.getItem('accessToken');
// if (token) {
// headers.set('x-auth-token', `${token}`);
// }
// return headers;
// },
// });
// Define a base query function with token refresh logic
// Define a base query function with token refresh logic, retry mechanism, and AbortController
export const baseQuery = async (args, api, extraOptions) => {
let result = await fetchBaseQuery({
baseUrl: import.meta.env.VITE_BAS_URL,
prepareHeaders: (headers) => {
const token = localStorage.getItem("accessToken");
if (token) {
headers.set("x-auth-token", token);
}
return headers;
},
})(args, api, extraOptions);
const fetchBase = fetchBaseQuery({
baseUrl: import.meta.env.VITE_BAS_URL,
credentials: 'include',
prepareHeaders: (headers) => {
headers.set('Content-Type', 'application/json');
return headers;
},
});
if (result.error && result.error.status === 403) {
// Handle token refresh
const refreshToken = localStorage.getItem("refreshToken");
console.log(refreshToken);
if (refreshToken) {
try {
const refreshResult = await fetchBaseQuery({
baseUrl: import.meta.env.VITE_BAS_URL,
})(
{
url: "/auth/user/regenerate-token",
method: "POST",
body: { refreshToken },
},
api,
extraOptions
);
const abortController = new AbortController();
extraOptions = {
...extraOptions,
signal: abortController.signal,
};
if (refreshResult.data) {
// Save new tokens
localStorage.setItem("accessToken", refreshResult?.data?.data?.access?.token);
localStorage.setItem("role", refreshResult?.data?.data?.role);
// console.log(refreshResult?.data?.data?.role);
let result = await fetchBase(args, api, extraOptions);
// Retry the original request with the new token
result = await fetchBaseQuery({
baseUrl: import.meta.env.VITE_BAS_URL,
prepareHeaders: (headers) => {
const token = localStorage.getItem("accessToken");
if (token) {
headers.set("x-auth-token", token);
}
return headers;
},
})(args, api, extraOptions);
}else{
if (result.error) {
if (result.error.status === 403) {
let retryCount = 0;
const maxRetries = import.meta.env.VITE_MAX_TRY_REGENRATE_TOKEN || 2;
console.log('refresh failed');
localStorage.clear();
window.location.href = '/login'; // Redirect to login page
while (retryCount < maxRetries) {
try {
const { data, error } = await fetchBase(
{
url: "/auth/user/regenerate-token",
method: "POST",
},
api,
{ ...extraOptions, signal: abortController.signal }
);
if (data) {
// Retry the original query after successful token regeneration
return await fetchBase(args, api, { ...extraOptions, signal: abortController.signal });
}
}
} catch (err) {
console.error("Failed to refresh token:", err);
localStorage.clear();
window.location.href = '/login'; // Redirect to login page
// Handle refresh failure (e.g., redirect to login)
}
}
}
throw error;
} catch (err) {
retryCount++;
if (retryCount >= maxRetries) {
console.error("Failed to refresh token after retries:", err);
abortController.abort();
localStorage.clear();
window.location.href = '/login'; // Redirect to login page
break;
}
}
}
} else if (result.error.status === 401) {
abortController.abort();
localStorage.clear();
window.location.href = '/login';
}
}
return result;
return result;
};
// Create an RTK Query API slice
export const apiSlice = createApi({
reducerPath: "api",
baseQuery: baseQuery,
endpoints: (builder) => ({
login: builder.mutation({
query: (credentials) => ({
url: "/auth/admin",
method: "POST",
body: credentials,
}),
async onQueryStarted(arg, { dispatch, queryFulfilled }) {
try {
const { data } = await queryFulfilled;
// Store tokens in local storage
localStorage.setItem("accessToken", data?.data?.access?.token);
localStorage.setItem("refreshToken", data?.data?.refresh?.token);
// localStorage.setItem('refreshTokenExp', data?.data?.refresh?.expires);
localStorage.setItem("accessTokenExp", data?.data?.access?.expires);
localStorage.setItem("role", data?.data?.role);
} catch (error) {
console.error("Login failed:", error);
}
},
}),
refreshToken: builder.mutation({
query: (refreshToken) => ({
url: "/auth/user/regenerate-token",
method: "POST",
body: { refreshToken },
}),
}),
reducerPath: "api",
baseQuery: baseQuery,
tagTypes: ["authProfile"],
endpoints: (builder) => ({
login: builder.mutation({
query: (credentials) => ({
url: "/auth/admin",
method: "POST",
body: credentials,
}),
async onQueryStarted(arg, { queryFulfilled }) {
try {
const { data } = await queryFulfilled;
localStorage.setItem("role", encryptString(data?.data?.role));
} catch (error) {
console.error("Login failed:", error);
}
},
}),
refreshToken: builder.mutation({
query: (refreshToken) => ({
url: "/auth/user/regenerate-token",
method: "POST",
body: { refreshToken },
}),
}),
logout: builder.mutation({
query: () => ({
url: "/auth/admin/logout",
method: "POST",
}),
}),
authProfile: builder.query({
query: () => `/auth/admin/profile`,
providesTags: (result) => result ? [{ type: 'Auth', id: 'profile' }] : [],
onQueryStarted: async (args, { queryFulfilled }) => {
try {
const { data } = await queryFulfilled; // Get the data from the query response
if (data?.role) {
localStorage.setItem('role', encryptString(data.role));
}
} catch (error) {
console.error('Error setting role in localStorage:', error);
}
},
}),
logout: builder.mutation({
query: () => ({
url: "/auth/admin/logout",
method: "POST",
}),
}),
}),
}),
});
export const { useLoginMutation, useRefreshTokenMutation, useLogoutMutation } = apiSlice;
export const { useLoginMutation, useRefreshTokenMutation, useLogoutMutation, useAuthProfileQuery } = apiSlice;

View File

@@ -17,6 +17,9 @@ import { deleteRequest } from "../Services/delete.request.service";
import { banInvestorDetails } from "../Services/ban.investor.service";
import { fawateerRequest } from "../Services/fawateer.request.service";
import { fawateerMaker } from "../Services/fawateer.maker.service";
import { sabAdminMaster } from "../Services/subadmin.service";
import { changePasswordMake } from "../Services/change.password.service";
import { forgetPasswordMake } from "../Services/forget.password.service";
export const store = configureStore({
reducer: {
@@ -35,6 +38,9 @@ export const store = configureStore({
[banInvestorDetails.reducerPath]: banInvestorDetails.reducer,
[fawateerRequest.reducerPath]: fawateerRequest.reducer,
[fawateerMaker.reducerPath]: fawateerMaker.reducer,
[sabAdminMaster.reducerPath]: sabAdminMaster.reducer,
[changePasswordMake.reducerPath]: changePasswordMake.reducer,
[forgetPasswordMake.reducerPath]: forgetPasswordMake.reducer,
// Add other reducers as needed
},
@@ -59,7 +65,9 @@ export const store = configureStore({
banInvestorDetails.middleware,
fawateerRequest.middleware,
fawateerMaker.middleware,
sabAdminMaster.middleware,
changePasswordMake.middleware,
forgetPasswordMake.middleware,
),
});