Compare commits

...

34 Commits

Author SHA1 Message Date
c72f3ece4e Merge pull request 'dev' (#24) from dev into main
Reviewed-on: #24
2024-12-20 12:26:22 +00:00
YasinShaikh123
85be7c891e update bugs 2024-12-20 17:17:18 +05:30
YasinShaikh123
e41d6b17b9 update env file ang bugs 2024-12-20 16:22:29 +05:30
3217605a0f Update README.md 2024-12-20 10:24:31 +00:00
b4d28387fa Update README.md 2024-12-20 09:36:28 +00:00
Swapnil Bendal
5411d4cd18 [fixed] - email message forget password 2024-12-12 17:47:57 +05:30
Swapnil Bendal
6a0aa17e2d [update]- profile page 2024-12-12 17:28:32 +05:30
Swapnil Bendal
471e4f32ab [fixed] - token server 2024-12-12 16:19:36 +05:30
YasinShaikh123
5d2c28f6ca update change password 2024-12-12 13:52:55 +05:30
YasinShaikh123
49f39e1c2c update subadmin 2024-12-12 13:39:19 +05:30
YasinShaikh123
f9c7bf9d5d update forget passsword 2024-12-12 13:20:12 +05:30
Swapnil Bendal
49beb9539a [fixed] - role 2024-12-11 20:30:52 +05:30
Swapnil Bendal
ed27ed6939 [update] - io cash, IO nav and pending 2024-12-11 19:36:06 +05:30
Swapnil Bendal
d84b3a0e35 [update] - role check 2024-12-11 18:47:43 +05:30
Swapnil Bendal
20c0c7840f [update] - constant 2024-12-11 15:45:56 +05:30
Swapnil Bendal
bbfd617b27 [update] - finalise 2024-12-11 14:54:13 +05:30
Swapnil Bendal
87e0716383 [added] - .env.exmple 2024-12-11 13:21:43 +05:30
Swapnil Bendal
c7d6a0fe36 [update] - token service 2024-12-11 12:59:19 +05:30
Swapnil Bendal
c83aaa411a Merge remote-tracking branch 'origin/Sprint-9' into audit-fix/swapnil 2024-12-11 11:42:24 +05:30
Swapnil Bendal
a2fe1435cb [update] - token service 2024-12-11 11:41:02 +05:30
6a9e0f9908 Merge pull request 'Yasin' (#23) from Yasin into Sprint-9
Reviewed-on: #23
2024-12-10 13:35:07 +00:00
YasinShaikh123
9c0b231e62 update changes 2024-12-09 20:10:47 +05:30
Swapnil Bendal
4ca28fd910 [untested] - code checkout in remote 2024-12-06 20:25:12 +05:30
YasinShaikh123
fed5125660 working create password 2024-12-06 20:04:20 +05:30
YasinShaikh123
8a297d02ef Merge branch 'Siddhesh' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into Yasin 2024-12-06 16:15:10 +05:30
YasinShaikh123
2c8965c16a working chenge password 2024-12-06 16:11:18 +05:30
9d4d5301e5 update 2024-12-06 16:09:36 +05:30
88dc9d14fe Merge pull request 'Yasin' (#22) from Yasin into Sprint-9
Reviewed-on: #22
2024-12-06 07:18:57 +00:00
YasinShaikh123
d567acfec8 update subadmin 2024-12-05 20:26:39 +05:30
YasinShaikh123
463325e603 update investor 2024-12-02 12:47:44 +05:30
84f869d9bd Merge branch 'release/sprint-8' of http://git.wdipl.com/Siddhesh.More/tanami-admin-panel into Siddhesh 2024-12-02 12:26:26 +05:30
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
95c629533e update 2024-11-14 12:18:54 +05:30
46 changed files with 2210 additions and 713 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"

104
README.md
View File

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

View File

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

View File

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

View File

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

View File

@@ -4,6 +4,16 @@ import { Spinner } from "@chakra-ui/react";
const Loader01 = () => { const Loader01 = () => {
return ( 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">
// <div className="dot-spinner__dot"></div> // <div className="dot-spinner__dot"></div>

View File

@@ -5,7 +5,7 @@ import { ChevronLeftIcon, ChevronRightIcon } from "@chakra-ui/icons";
const Pagination = ({ const Pagination = ({
pageSize, pageSize,
setPageSize, setPageSize,
totalItems, totalItems = 1,
isLoading, isLoading,
setCurrentPage, setCurrentPage,
currentPage, currentPage,
@@ -84,7 +84,7 @@ const Pagination = ({
onClick={paginationNext} onClick={paginationNext}
className="link pointer" className="link pointer"
isDisabled={currentPage === totalPages} isDisabled={currentPage === totalPages}
aria-label="Next Page" aria-label="Next Page"
/> />
</HStack> </HStack>
</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 dns from "node:dns"
import * as XLSX from 'xlsx'; 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); return (currentPage - 1) * pageSize + (index + 1);
}; };
@@ -11,7 +11,7 @@ export function getTomorrowDate() {
const today = new Date(); const today = new Date();
const tomorrow = new Date(today); const tomorrow = new Date(today);
tomorrow.setDate(today.getDate() + 1); tomorrow.setDate(today.getDate() + 1);
// Format the date as YYYY-MM-DD (ISO 8601) // Format the date as YYYY-MM-DD (ISO 8601)
return tomorrow.toISOString().split('T')[0]; 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 // Parse the UTC datetime string into a Date object
const targetDate = new Date(utcDateString); const targetDate = new Date(utcDateString);
const now = new Date(); const now = new Date();
@@ -56,7 +56,7 @@ export function removeTrailingZeros(value) {
const remainingMinutes = minutes % 60; const remainingMinutes = minutes % 60;
const remainingSeconds = seconds % 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) => { return new Promise((resolve, reject) => {
dns.resolveMx(domain, (err, mxRecords) => { dns.resolveMx(domain, (err, mxRecords) => {
if (err) { if (err) {
reject(err); reject(err);
return; return;
} }
const addresses = mxRecords.map((mxRecord) => mxRecord.exchange); const addresses = mxRecords.map((mxRecord) => mxRecord.exchange);
resolve(addresses); resolve(addresses);
}); });
}); });
} }
// Async function to check email address validity // Async function to check email address validity
export async function checkEmailValidity(email) { export async function checkEmailValidity(email) {
try { try {
const domain = email?.split("@")[1]; const domain = email?.split('@')[1];
const addresses = await resolveMx(domain, "MX"); const addresses = await resolveMx(domain, 'MX');
console.log(addresses);
if (addresses && addresses?.length > 0) { if (addresses && addresses?.length > 0) {
return true; return true;
} }
return false; // No MX record exists return false; // No MX record exists
} catch (err) { } 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 // Function to convert timestamp to readable date format in Gulf timezone
export function formatTimestampInGulfTimezone(timestamp) { export function formatTimestampInGulfTimezone(timestamp) {
const date = new Date(timestamp); const date = new Date(timestamp);
const options = { const options = {
year: 'numeric', year: 'numeric',
month: 'long', month: 'long',
day: 'numeric', day: 'numeric',
hour: '2-digit', hour: '2-digit',
minute: '2-digit', minute: '2-digit',
second: '2-digit', second: '2-digit',
timeZone: 'Asia/Dubai', // Gulf Standard Time (GST) timezone timeZone: 'Asia/Dubai', // Gulf Standard Time (GST) timezone
timeZoneName: 'short' timeZoneName: 'short'
}; };
return date.toLocaleDateString('en-GB', options); return date.toLocaleDateString('en-GB', options);
} }
@@ -163,7 +166,7 @@ const getNestedValue = (obj, key) => {
export const exportToExcel = (data, headers) => { export const exportToExcel = (data, headers) => {
const flattenedData = data.map((item) => { const flattenedData = data.map((item) => {
const newItem = {}; const newItem = {};
// Loop through customHeaders and get the correct values // Loop through customHeaders and get the correct values
headers.forEach((header) => { headers.forEach((header) => {
newItem[header.label] = getNestedValue(item, header.key); // Use the helper function 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 // Now pass flattenedData to your Excel library to generate the file
// Assuming you're using a library like `xlsx` for this part: // Assuming you're using a library like `xlsx` for this part:
const worksheet = XLSX.utils.json_to_sheet(flattenedData); const worksheet = XLSX.utils.json_to_sheet(flattenedData);
const workbook = XLSX.utils.book_new(); const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1"); XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
@@ -218,4 +221,25 @@ export function formatDateToYYYYMMDD(dateString) {
// Combine the formatted parts // Combine the formatted parts
return `${year}-${month}-${day}`; 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 export const IMAGE_URI = import.meta.env.VITE_API_IMAGE_URL

View File

@@ -1,21 +1,48 @@
import React, { useContext, useEffect, useState } from "react"; 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 { 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 { 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, TbListDetails,
TbReportMoney, TbReportMoney,
TbTransactionDollar, TbTransactionDollar
} from "react-icons/tb"; } from "react-icons/tb";
import { TbArrowBadgeRightFilled } from "react-icons/tb"; import { VscSymbolClass } from "react-icons/vsc";
import { ArrowBackIcon, ArrowLeftIcon, ArrowRightIcon, AtSignIcon } from "@chakra-ui/icons";
import { import {
Link, Link,
NavLink, NavLink,
@@ -24,70 +51,21 @@ import {
useLocation, useLocation,
useNavigate, useNavigate,
} from "react-router-dom"; } 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 HeaderMain from "../Components/HeaderMain";
import { IoMdSwap } from "react-icons/io"; import GlobalStateContext from "../Contexts/GlobalStateContext";
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 CreateRequest from "../Pages/Fawateer/CreateRequest"; 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 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 { useProfileQuery } from "../Services/io.service";
import { useLogoutMutation } from "../Services/token.serivce";
const DashboardLayout = ({ isOnline }) => { const DashboardLayout = ({ isOnline }) => {
const userRole = localStorage.getItem("role");
const navigate = useNavigate(); const navigate = useNavigate();
const dispach = useDispatch();
const location = useLocation(); const location = useLocation();
const path = location.pathname; const path = location.pathname;
const [isDrawerOpen, setIsDrawerOpen] = useState(false); const [isDrawerOpen, setIsDrawerOpen] = useState(false);
@@ -104,15 +82,15 @@ const DashboardLayout = ({ isOnline }) => {
const { data, refetch } = useProfileQuery(); const { data, refetch } = useProfileQuery();
useEffect(() => { // useEffect(() => {
if ( // if (
!localStorage.getItem("accessToken") && // !localStorage.getItem("accessToken") &&
!localStorage.getItem("refreshToken") // !localStorage.getItem("refreshToken")
) { // ) {
logOutHandler(); // logOutHandler();
return navigate("/login"); // return navigate("/login");
} // }
}, []); // }, []);
useEffect(() => { useEffect(() => {
const savedIndex = localStorage.getItem("openAccordionIndex"); const savedIndex = localStorage.getItem("openAccordionIndex");
@@ -151,7 +129,9 @@ const DashboardLayout = ({ isOnline }) => {
await logout(); await logout();
localStorage.clear(); localStorage.clear();
navigate("/login"); navigate("/login");
} catch (error) {} } catch (error) {
console.log(error);
}
}; };
// // Function to get the title based on the route // // Function to get the title based on the route
@@ -165,24 +145,24 @@ const DashboardLayout = ({ isOnline }) => {
<RiMoneyDollarBoxLine className="h4 m-0" /> Sponsor <RiMoneyDollarBoxLine className="h4 m-0" /> Sponsor
</span> </span>
); );
case path.startsWith("/email"): case path.startsWith("/email"):
return ( return (
<span className="d-flex align-items-end gap-2"> <span className="d-flex align-items-end gap-2">
<AtSignIcon className="h4 m-0" /> Email Notifiation <AtSignIcon className="h4 m-0" /> Email Notifiation
</span> </span>
); );
case path.startsWith("/investment-type"): case path.startsWith("/investment-type"):
return ( return (
<span className="d-flex align-items-end gap-2"> <span className="d-flex align-items-end gap-2">
<VscSymbolClass className="h4 m-0" /> Investment Type <VscSymbolClass className="h4 m-0" /> Investment Type
</span> </span>
); );
case path.startsWith("/profile"): case path.startsWith("/profile"):
return ( return (
<span className="d-flex align-items-end gap-2"> <span className="d-flex align-items-end gap-2">
<CgProfile className="h4 m-0" /> Profile <CgProfile className="h4 m-0" /> Profile
</span> </span>
); );
case path.startsWith("/exchange-rate"): case path.startsWith("/exchange-rate"):
return ( return (
<span className="d-flex align-items-end gap-2"> <span className="d-flex align-items-end gap-2">
@@ -241,7 +221,7 @@ const DashboardLayout = ({ isOnline }) => {
Deposite Request Deposite Request
</span> </span>
); );
case path.startsWith("/fawateer"): case path.startsWith("/fawateer"):
return ( return (
<span className="d-flex align-items-end gap-2"> <span className="d-flex align-items-end gap-2">
@@ -249,13 +229,13 @@ const DashboardLayout = ({ isOnline }) => {
Fawateer Deposit Fawateer Deposit
</span> </span>
); );
case path.startsWith("/fawateer-history"): case path.startsWith("/fawateer-history"):
return ( return (
<span className="d-flex align-items-end gap-2"> <span className="d-flex align-items-end gap-2">
<RiMoneyDollarBoxLine className="h4 m-0 fw-normal" /> <RiMoneyDollarBoxLine className="h4 m-0 fw-normal" />
Fawateer Deposit Fawateer Deposit
</span> </span>
); );
case path.startsWith("/withdraw-request"): case path.startsWith("/withdraw-request"):
return ( return (
@@ -369,6 +349,13 @@ const DashboardLayout = ({ isOnline }) => {
Deletion request Deletion request
</span> </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: default:
if (path.startsWith("/community/view/")) { if (path.startsWith("/community/view/")) {
@@ -398,6 +385,19 @@ const DashboardLayout = ({ isOnline }) => {
return <SplashScreen />; 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 ( return (
<Box <Box
style={{ style={{
@@ -515,7 +515,7 @@ const DashboardLayout = ({ isOnline }) => {
index={openIndex} index={openIndex}
onChange={handleAccordionChange} onChange={handleAccordionChange}
> >
{nav.map(({ title, type, Icon, submenu, path }, index) => { {filteredNav.map(({ title, type, Icon, submenu, path }, index) => {
if (type === "accordion") { if (type === "accordion") {
return ( return (
<AccordionItem key={index} border={"none"}> <AccordionItem key={index} border={"none"}>
@@ -788,6 +788,7 @@ const AppContent = ({ data }) => {
) )
} }
/> />
<Route path="*" element={<NotFound />} /> <Route path="*" element={<NotFound />} />
</Routes> </Routes>
); );

View File

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

View File

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

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

@@ -20,6 +20,7 @@ import AddCaseDetails from "./AddCaseDetails";
import { useUpdateIOCaseMutation } from "../../../../Services/io.service"; import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
import ToastBox from "../../../../Components/ToastBox"; import ToastBox from "../../../../Components/ToastBox";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { encryptString, isMaker } from "../../../../Constants/Constants";
const IOCashDetails = () => { const IOCashDetails = () => {
const params = useParams(); const params = useParams();
@@ -87,7 +88,8 @@ const IOCashDetails = () => {
Pending Pending
{IODetails?.ioCashStatusHistory?.Pending.length > 0 && ( {IODetails?.ioCashStatusHistory?.Pending.length > 0 && (
<Badge rounded={"sm"} colorScheme="forestGreen" ms={2}> <Badge rounded={"sm"} colorScheme="forestGreen" ms={2}>
{IODetails?.ioCashStatusHistory?.Pending.length !== 0 && IODetails?.ioCashStatusHistory?.Pending.length} {IODetails?.ioCashStatusHistory?.Pending.length !== 0 &&
IODetails?.ioCashStatusHistory?.Pending.length}
</Badge> </Badge>
)} )}
{/* <Badge rounded={"sm"} colorScheme="forestGreen" ms={2}> {/* <Badge rounded={"sm"} colorScheme="forestGreen" ms={2}>
@@ -105,7 +107,7 @@ const IOCashDetails = () => {
</Tab> </Tab>
</TabList> </TabList>
{IODetails?.isInvestedAmount {IODetails?.isInvestedAmount
? localStorage?.getItem("role") === "Maker" && ( ? isMaker() && (
<Button <Button
onClick={handleAdd} onClick={handleAdd}
leftIcon={<AddIcon />} leftIcon={<AddIcon />}

View File

@@ -38,6 +38,7 @@ import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
import RequestApproveModal from "./RequestApproveModal"; import RequestApproveModal from "./RequestApproveModal";
import RequestRejectModal from "./RequestRejectModal"; import RequestRejectModal from "./RequestRejectModal";
import AddCaseDetails from "./AddCaseDetails"; import AddCaseDetails from "./AddCaseDetails";
import { encryptString } from "../../../../Constants/Constants";
const formatDate = (date) => new Date(date).toLocaleDateString(); const formatDate = (date) => new Date(date).toLocaleDateString();
@@ -104,7 +105,7 @@ const Pending = () => {
"Comments", "Comments",
"Update By", "Update By",
"Update On", "Update On",
...(localStorage?.getItem('role')!=="Maker" ? ["Actions"] : []), ...(localStorage?.getItem('role')!==encryptString(import.meta.env.VITE_VITE_MAKER) ? ["Actions"] : []),
]; ];
@@ -166,7 +167,7 @@ const Pending = () => {
), ),
Actions: ( Actions: (
<Box display={"flex"} justifyContent={"center"}> <Box display={"flex"} justifyContent={"center"}>
{localStorage?.getItem("role") !== "Maker" ? <Box> {localStorage?.getItem("role") !== encryptString(import.meta.env.VITE_VITE_MAKER) ? <Box>
{index===0&&<Box display={"flex"} justifyContent={"center"} gap={2}> {index===0&&<Box display={"flex"} justifyContent={"center"} gap={2}>
<Tooltip <Tooltip
rounded={"sm"} rounded={"sm"}

View File

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

View File

@@ -4,10 +4,7 @@ import {
Box, Box,
Button, Button,
HStack, HStack,
Input, Input,
Table,
Tag,
Tbody,
Text, Text,
Th, Th,
Tooltip, Tooltip,
@@ -22,7 +19,7 @@ import { OPACITY_ON_LOAD } from "../../../../Layout/animations";
import NormalTable from "../../../../Components/DataTable/NormalTable"; import NormalTable from "../../../../Components/DataTable/NormalTable";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext"; import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import CustomAlertDialog from "../../../../Components/CustomAlertDialog"; import CustomAlertDialog from "../../../../Components/CustomAlertDialog";
import * as XLSX from "xlsx"; import * as XLSX from "xlsx";
import ToastBox from "../../../../Components/ToastBox"; import ToastBox from "../../../../Components/ToastBox";
import AddCashDetails from "../AddCashDetails"; import AddCashDetails from "../AddCashDetails";
import { debounce } from "../../../Admin/Contact"; import { debounce } from "../../../Admin/Contact";

View File

@@ -75,6 +75,7 @@ import ToastBox from "../../../../Components/ToastBox";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import AddNavDetails from "./AddNavDetails"; import AddNavDetails from "./AddNavDetails";
import { useUpdateIOCaseMutation } from "../../../../Services/io.service"; import { useUpdateIOCaseMutation } from "../../../../Services/io.service";
import { encryptString, isMaker } from "../../../../Constants/Constants";
const IONAVDetails = () => { const IONAVDetails = () => {
const params = useParams(); const params = useParams();
@@ -152,7 +153,7 @@ const IONAVDetails = () => {
</Tab> </Tab>
</TabList> </TabList>
{IODetails?.isInvestedAmount {IODetails?.isInvestedAmount
? localStorage?.getItem("role") === "Maker" && ( ? isMaker() && (
<Button <Button
onClick={handleAdd} onClick={handleAdd}
leftIcon={<AddIcon />} leftIcon={<AddIcon />}

View File

@@ -22,6 +22,7 @@ import ToastBox from "../../../../Components/ToastBox";
import AddNavDetails from "./AddNavDetails"; import AddNavDetails from "./AddNavDetails";
import RequestApproveModal from "./RequestApproveModal"; import RequestApproveModal from "./RequestApproveModal";
import RequestRejectModal from "./RequestRejectModal"; import RequestRejectModal from "./RequestRejectModal";
import { encryptString } from "../../../../Constants/Constants";
const formatDate = (date) => new Date(date).toLocaleDateString(); const formatDate = (date) => new Date(date).toLocaleDateString();
@@ -90,7 +91,7 @@ const Pending = () => {
"Investment Closed", "Investment Closed",
"Comments", "Comments",
"Updated By", "Updated By",
...(localStorage?.getItem("role") !== "Maker" ? ["Status"] : []), ...(localStorage?.getItem("role") !== encryptString(import.meta.env.VITE_VITE_MAKER) ? ["Status"] : []),
]; ];
const extractedArray = filteredData?.map((item, index) => ({ const extractedArray = filteredData?.map((item, index) => ({

View File

@@ -27,6 +27,7 @@ import ViewAmountInvested from "./ViewAmountInvested";
import ViewDistributionInvestor from "./ViewDistributionInvestor"; import ViewDistributionInvestor from "./ViewDistributionInvestor";
import ViewExit from "./ViewExit"; import ViewExit from "./ViewExit";
import ViewCancel from "./ViewCancel"; import ViewCancel from "./ViewCancel";
import { encryptString, isMaker } from "../../../../Constants/Constants";
const formatDate = (date) => new Date(date).toLocaleDateString(); const formatDate = (date) => new Date(date).toLocaleDateString();
@@ -127,11 +128,7 @@ const Pending = () => {
</Text> </Text>
), ),
"Transaction Date": ( "Transaction Date": (
<Text <Text as={"span"} color={"gray.600"} fontWeight={"500"}>
as={"span"}
color={"gray.600"}
fontWeight={"500"}
>
{formatDate(item?.transactionDate)} {formatDate(item?.transactionDate)}
</Text> </Text>
), ),
@@ -152,8 +149,13 @@ const Pending = () => {
</Text> </Text>
), ),
"Created By": ( "Created By": (
<Text <Text
textTransform={'capitalize'} w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}> textTransform={"capitalize"}
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
{item?.creator?.firstName} {item?.creator?.firstName}
</Text> </Text>
), ),
@@ -163,8 +165,13 @@ const Pending = () => {
</Text> </Text>
), ),
"Approved By": ( "Approved By": (
<Text <Text
textTransform={'capitalize'} w={"100px"} as={"span"} color={"gray.800"} fontWeight={"500"}> textTransform={"capitalize"}
w={"100px"}
as={"span"}
color={"gray.800"}
fontWeight={"500"}
>
{item?.modifier?.firstName} {item?.modifier?.firstName}
</Text> </Text>
), ),
@@ -195,7 +202,11 @@ const Pending = () => {
} }
}} }}
> >
{localStorage?.getItem("role") === "Maker" ? <ViewIcon me={"4px"} /> : null} {localStorage?.getItem("role") === "Maker" ? "View" : "Approve / Reject"} {isMaker() ? <ViewIcon me={"4px"} /> : null}{" "}
{localStorage?.getItem("role") ===
encryptString(import.meta.env.VITE_VITE_MAKER)
? "View"
: "Approve / Reject"}
</Button> </Button>
</Box> </Box>
), ),
@@ -262,20 +273,12 @@ const Pending = () => {
id={actionId} id={actionId}
/> />
<ViewDistributionInvestor <ViewDistributionInvestor
isOpen={isDistInvestorOpen} isOpen={isDistInvestorOpen}
onClose={onDistInvestorClose} onClose={onDistInvestorClose}
id={actionId}
/>
<ViewExit
isOpen={isExitOpen}
onClose={onExitClose}
id={actionId} id={actionId}
/> />
<ViewCancel <ViewExit isOpen={isExitOpen} onClose={onExitClose} id={actionId} />
isOpen={isCancelOpen} <ViewCancel isOpen={isCancelOpen} onClose={onCancelClose} id={actionId} />
onClose={onCancelClose}
id={actionId}
/>
<RequestApproveModal <RequestApproveModal
// data={data?.data?.rows} // data={data?.data?.rows}
isOpen={isConfirmOpen} isOpen={isConfirmOpen}

View File

@@ -27,6 +27,7 @@ import CurrencyInput from "../../../../Components/CurrencyInput";
import RequestRejectModal from "./RequestRejectModal"; import RequestRejectModal from "./RequestRejectModal";
import ApproveInvestedModal from "./ApproveInvestedModal"; import ApproveInvestedModal from "./ApproveInvestedModal";
import { formatDate } from "../../../Master/Sponser/Sponsers"; import { formatDate } from "../../../Master/Sponser/Sponsers";
import { encryptString } from "../../../../Constants/Constants";
// Validation schema // Validation schema
const validationSchema = yup.object().shape({ const validationSchema = yup.object().shape({
@@ -236,7 +237,7 @@ const ViewAmountInvested = ({ isOpen, onClose, id: investorId }) => {
/> />
</FormControl> </FormControl>
{localStorage?.getItem("role") !== "Maker" && <ModalFooter> {localStorage?.getItem("role") !== encryptString(import.meta.env.VITE_VITE_MAKER) && <ModalFooter>
<Box display={"flex"} justifyContent={"center"} gap={2}> <Box display={"flex"} justifyContent={"center"} gap={2}>
<Button <Button
rounded={"sm"} rounded={"sm"}

View File

@@ -35,6 +35,7 @@ import {
import GlobalStateContext from "../../../../Contexts/GlobalStateContext"; import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import ApprovedCancelTransaction from "./ApprovedCancelTransaction"; import ApprovedCancelTransaction from "./ApprovedCancelTransaction";
import RequestRejectModal from "./RequestRejectModal"; import RequestRejectModal from "./RequestRejectModal";
import { encryptString } from "../../../../Constants/Constants";
const ViewCancel = ({ isOpen, onClose,id:cancleId }) => { const ViewCancel = ({ isOpen, onClose,id:cancleId }) => {
const params = useParams(); const params = useParams();
@@ -311,7 +312,7 @@ import RequestRejectModal from "./RequestRejectModal";
data={extractedArray} data={extractedArray}
/> />
</ModalBody> </ModalBody>
{localStorage?.getItem("role") !== "Maker" && <ModalFooter pt={0}> {localStorage?.getItem("role") !== encryptString(import.meta.env.VITE_VITE_MAKER) && <ModalFooter pt={0}>
<Box display={"flex"} justifyContent={"center"} gap={2}> <Box display={"flex"} justifyContent={"center"} gap={2}>
<Button <Button
rounded={"sm"} rounded={"sm"}

View File

@@ -23,6 +23,7 @@ import { yupResolver } from "@hookform/resolvers/yup";
import GlobalStateContext from "../../../../Contexts/GlobalStateContext"; import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import ApproveDistrubationModal from "./ApproveDistrubationModal"; import ApproveDistrubationModal from "./ApproveDistrubationModal";
import RequestRejectModal from "./RequestRejectModal"; import RequestRejectModal from "./RequestRejectModal";
import { encryptString } from "../../../../Constants/Constants";
const ViewDistributionInvestor = ({ isOpen, onClose,id:exitId }) => { const ViewDistributionInvestor = ({ isOpen, onClose,id:exitId }) => {
const params = useParams(); const params = useParams();
@@ -218,7 +219,7 @@ const ViewDistributionInvestor = ({ isOpen, onClose,id:exitId }) => {
/> />
</ModalBody> </ModalBody>
{/* ...(localStorage?.getItem("role") !== "Maker" ? ["Status"] : []), */} {/* ...(localStorage?.getItem("role") !== "Maker" ? ["Status"] : []), */}
{localStorage?.getItem("role") !== "Maker" &&<ModalFooter pt={0}> {localStorage?.getItem("role") !== encryptString(import.meta.env.VITE_VITE_MAKER) &&<ModalFooter pt={0}>
<Box display={"flex"} justifyContent={"center"} gap={2}> <Box display={"flex"} justifyContent={"center"} gap={2}>
<Button <Button
rounded={"sm"} rounded={"sm"}

View File

@@ -43,6 +43,7 @@ import {
import GlobalStateContext from "../../../../Contexts/GlobalStateContext"; import GlobalStateContext from "../../../../Contexts/GlobalStateContext";
import ApprovedExit from "./ApprovedExit"; import ApprovedExit from "./ApprovedExit";
import RequestRejectModal from "./RequestRejectModal"; import RequestRejectModal from "./RequestRejectModal";
import { encryptString } from "../../../../Constants/Constants";
const ViewExit = ({ isOpen, onClose ,id:investerId}) => { const ViewExit = ({ isOpen, onClose ,id:investerId}) => {
const params = useParams(); const params = useParams();
@@ -273,7 +274,7 @@ import RequestRejectModal from "./RequestRejectModal";
/> />
{/* ) } */} {/* ) } */}
</ModalBody> </ModalBody>
{localStorage?.getItem("role") !== "Maker" && <ModalFooter pt={0}> {localStorage?.getItem("role") !== encryptString(import.meta.env.VITE_VITE_MAKER) && <ModalFooter pt={0}>
<Box display={"flex"} justifyContent={"center"} gap={2}> <Box display={"flex"} justifyContent={"center"} gap={2}>
<Button <Button
rounded={"sm"} rounded={"sm"}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -316,7 +316,7 @@ const AddInvestmentType = () => {
<Box display={"flex"} justifyContent={"space-between"} alignItems={"center"} mt={5} px={4} mb={5}> <Box display={"flex"} justifyContent={"space-between"} alignItems={"center"} mt={5} px={4} mb={5}>
<Text fontSize={"sm"} mb={0} onClick={() => navigate(-1)} cursor={"pointer"}> <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> </Text>
<SwitchButton isSwitchOn={isSwitchOn} setIsSwitchOn={setIsSwitchOn} /> <SwitchButton isSwitchOn={isSwitchOn} setIsSwitchOn={setIsSwitchOn} />
</Box> </Box>

View File

@@ -301,7 +301,7 @@ const AddSponser = () => {
{/* ===================== [Switch Button] ======================== */} {/* ===================== [Switch Button] ======================== */}
<Box display={"flex"} justifyContent={"space-between"} alignItems={"center"} mt={5} px={4}> <Box display={"flex"} justifyContent={"space-between"} alignItems={"center"} mt={5} px={4}>
<Text fontSize={"sm"} mb={0} onClick={() => navigate(-1)} cursor={"pointer"}> <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> </Text>
<SwitchButton isSwitchOn={isSwitchOn} setIsSwitchOn={setIsSwitchOn} /> <SwitchButton isSwitchOn={isSwitchOn} setIsSwitchOn={setIsSwitchOn} />
</Box> </Box>

View File

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

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

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

View File

@@ -46,6 +46,8 @@ import EmailNotification from "../Pages/EmailNotification/EmailNotification";
import User from "../Pages/User/User"; import User from "../Pages/User/User";
import AddUser from "../Pages/User/AddUser"; import AddUser from "../Pages/User/AddUser";
import Profile from "../Pages/Profile/Profile"; import Profile from "../Pages/Profile/Profile";
import SubAdmin from "../Pages/SubAdmin/SubAdmin";
import SubAdminUpdateCreate from "../Pages/SubAdmin/SubAdminUpdateCreate";
export const RouteLink = [ export const RouteLink = [
// =============[ Tanami ]================ // =============[ Tanami ]================
@@ -123,6 +125,9 @@ export const RouteLink = [
// { path: "/bank-details", Component: UnderConstruction }, // { path: "/bank-details", Component: UnderConstruction },
{ path: "/bank-details/edit-bank-details/:id", Component: EditBankDetails }, { path: "/bank-details/edit-bank-details/:id", Component: EditBankDetails },
{ path: "/profile", Component: Profile }, { 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: "/fawateer-approver", Component: ApproveRequest },
// { path: "/approver-history", Component: ApproveHistory }, // { 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

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

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 { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { encryptString } from '../Constants/Constants'
// Define a base query function with RTK Query // Define a base query function with token refresh logic, retry mechanism, and AbortController
// 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
export const baseQuery = async (args, api, extraOptions) => { export const baseQuery = async (args, api, extraOptions) => {
let result = await fetchBaseQuery({ const fetchBase = fetchBaseQuery({
baseUrl: import.meta.env.VITE_BAS_URL, baseUrl: import.meta.env.VITE_BAS_URL,
prepareHeaders: (headers) => { credentials: 'include',
const token = localStorage.getItem("accessToken"); prepareHeaders: (headers) => {
if (token) { headers.set('Content-Type', 'application/json');
headers.set("x-auth-token", token); return headers;
} },
return headers; });
},
})(args, api, extraOptions);
if (result.error && result.error.status === 403) { const abortController = new AbortController();
// Handle token refresh extraOptions = {
const refreshToken = localStorage.getItem("refreshToken"); ...extraOptions,
console.log(refreshToken); signal: abortController.signal,
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
);
if (refreshResult.data) { let result = await fetchBase(args, api, extraOptions);
// Save new tokens
localStorage.setItem("accessToken", refreshResult?.data?.data?.access?.token);
// localStorage.setItem("role", refreshResult?.data?.data?.role);
// console.log(refreshResult?.data?.data?.role);
// Retry the original request with the new token if (result.error) {
result = await fetchBaseQuery({ if (result.error.status === 403) {
baseUrl: import.meta.env.VITE_BAS_URL, let retryCount = 0;
prepareHeaders: (headers) => { const maxRetries = import.meta.env.VITE_MAX_TRY_REGENRATE_TOKEN || 2;
const token = localStorage.getItem("accessToken");
if (token) {
headers.set("x-auth-token", token);
}
return headers;
},
})(args, api, extraOptions);
}else{
console.log('refresh failed'); while (retryCount < maxRetries) {
localStorage.clear(); try {
window.location.href = '/login'; // Redirect to login page 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 });
}
} throw error;
} catch (err) { } catch (err) {
console.error("Failed to refresh token:", err); retryCount++;
localStorage.clear(); if (retryCount >= maxRetries) {
window.location.href = '/login'; // Redirect to login page console.error("Failed to refresh token after retries:", err);
// Handle refresh failure (e.g., redirect to login) 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 // Create an RTK Query API slice
export const apiSlice = createApi({ export const apiSlice = createApi({
reducerPath: "api", reducerPath: "api",
baseQuery: baseQuery, baseQuery: baseQuery,
endpoints: (builder) => ({ tagTypes: ["authProfile"],
login: builder.mutation({ endpoints: (builder) => ({
query: (credentials) => ({
url: "/auth/admin", login: builder.mutation({
method: "POST", query: (credentials) => ({
body: credentials, url: "/auth/admin",
}), method: "POST",
async onQueryStarted(arg, { dispatch, queryFulfilled }) { body: credentials,
try { }),
const { data } = await queryFulfilled; async onQueryStarted(arg, { queryFulfilled }) {
// Store tokens in local storage try {
localStorage.setItem("accessToken", data?.data?.access?.token); const { data } = await queryFulfilled;
localStorage.setItem("role", encryptString(data?.data?.role));
localStorage.setItem("refreshToken", data?.data?.refresh?.token); } catch (error) {
// localStorage.setItem('refreshTokenExp', data?.data?.refresh?.expires); console.error("Login failed:", error);
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",
refreshToken: builder.mutation({ method: "POST",
query: (refreshToken) => ({ body: { 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 { banInvestorDetails } from "../Services/ban.investor.service";
import { fawateerRequest } from "../Services/fawateer.request.service"; import { fawateerRequest } from "../Services/fawateer.request.service";
import { fawateerMaker } from "../Services/fawateer.maker.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({ export const store = configureStore({
reducer: { reducer: {
@@ -35,6 +38,9 @@ export const store = configureStore({
[banInvestorDetails.reducerPath]: banInvestorDetails.reducer, [banInvestorDetails.reducerPath]: banInvestorDetails.reducer,
[fawateerRequest.reducerPath]: fawateerRequest.reducer, [fawateerRequest.reducerPath]: fawateerRequest.reducer,
[fawateerMaker.reducerPath]: fawateerMaker.reducer, [fawateerMaker.reducerPath]: fawateerMaker.reducer,
[sabAdminMaster.reducerPath]: sabAdminMaster.reducer,
[changePasswordMake.reducerPath]: changePasswordMake.reducer,
[forgetPasswordMake.reducerPath]: forgetPasswordMake.reducer,
// Add other reducers as needed // Add other reducers as needed
}, },
@@ -59,7 +65,9 @@ export const store = configureStore({
banInvestorDetails.middleware, banInvestorDetails.middleware,
fawateerRequest.middleware, fawateerRequest.middleware,
fawateerMaker.middleware, fawateerMaker.middleware,
sabAdminMaster.middleware,
changePasswordMake.middleware,
forgetPasswordMake.middleware,
), ),
}); });