This commit is contained in:
2024-08-08 19:38:17 +05:30
parent 9da83b8297
commit 26363e2b0c
16 changed files with 311 additions and 83 deletions

View File

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

View File

@@ -0,0 +1,105 @@
/* From Uiverse.io by abrahamcalsin */
.dot-spinner {
--uib-size: 2.8rem;
--uib-speed: .9s;
--uib-color: #004717;
position: relative;
display: flex;
align-items: center;
justify-content: flex-start;
height: var(--uib-size);
width: var(--uib-size);
}
.dot-spinner__dot {
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: flex-start;
height: 100%;
width: 100%;
}
.dot-spinner__dot::before {
content: '';
height: 20%;
width: 20%;
border-radius: 50%;
background-color: var(--uib-color);
transform: scale(0);
opacity: 0.5;
animation: pulse0112 calc(var(--uib-speed) * 1.111) ease-in-out infinite;
box-shadow: 0 0 20px rgba(18, 31, 53, 0.3);
}
.dot-spinner__dot:nth-child(2) {
transform: rotate(45deg);
}
.dot-spinner__dot:nth-child(2)::before {
animation-delay: calc(var(--uib-speed) * -0.875);
}
.dot-spinner__dot:nth-child(3) {
transform: rotate(90deg);
}
.dot-spinner__dot:nth-child(3)::before {
animation-delay: calc(var(--uib-speed) * -0.75);
}
.dot-spinner__dot:nth-child(4) {
transform: rotate(135deg);
}
.dot-spinner__dot:nth-child(4)::before {
animation-delay: calc(var(--uib-speed) * -0.625);
}
.dot-spinner__dot:nth-child(5) {
transform: rotate(180deg);
}
.dot-spinner__dot:nth-child(5)::before {
animation-delay: calc(var(--uib-speed) * -0.5);
}
.dot-spinner__dot:nth-child(6) {
transform: rotate(225deg);
}
.dot-spinner__dot:nth-child(6)::before {
animation-delay: calc(var(--uib-speed) * -0.375);
}
.dot-spinner__dot:nth-child(7) {
transform: rotate(270deg);
}
.dot-spinner__dot:nth-child(7)::before {
animation-delay: calc(var(--uib-speed) * -0.25);
}
.dot-spinner__dot:nth-child(8) {
transform: rotate(315deg);
}
.dot-spinner__dot:nth-child(8)::before {
animation-delay: calc(var(--uib-speed) * -0.125);
}
@keyframes pulse0112 {
0%,
100% {
transform: scale(0);
opacity: 0.5;
}
50% {
transform: scale(1);
opacity: 1;
}
}

View File

@@ -1,5 +1,6 @@
import { Box, Spinner, Text } from "@chakra-ui/react";
import React from "react";
import './FullscreenLoaders.css'
const FullscreenLoaders = ({height}) => {
return (
@@ -11,14 +12,17 @@ const FullscreenLoaders = ({height}) => {
w={"100%"}
h={height ? height: "100vh"}
gap={4}
><Spinner
thickness='3px'
speed='0.65s'
emptyColor='green.100'
color='#004717'
size='lg'
/>
<Text color='#004717' fontSize={'md'} fontWeight={500}>Loading...</Text>
><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>
{/* <Text color='#004717' fontSize={'md'} fontWeight={500}>Loading...</Text> */}
</Box>
);
};

View File

@@ -107,7 +107,7 @@ const DashboardLayout = ({ isOnline }) => {
// Set a timer to hide the splash screen after 3 seconds
const timer = setTimeout(() => {
setSplashVisible(false);
},300); // 3000ms = 3 seconds
},1000); // 3000ms = 3 seconds
// Cleanup the timer
return () => clearTimeout(timer);

47
src/Pages/Dashbaord.jsx Normal file
View File

@@ -0,0 +1,47 @@
import { Box, Icon, Text } from '@chakra-ui/react'
import React from 'react'
import { RiMoneyDollarBoxLine } from 'react-icons/ri'
const Dashbaord = () => {
return (
<Box>
<Box display={'flex'} gap={6} w={'100%'} pt={3} pe={3} >
<Box boxShadow={'lg'} color={"#fff"} p={3} rounded={'xl'} w={'25%'} display={'flex'} bg={'#004118'} flexDirection={'column'} alignItems={'start'} gap={3} >
<Icon as={RiMoneyDollarBoxLine} boxSize={12} />
<Text as={'span'} fontSize={'xs'}>Total Investors</Text>
<Text as={'span'} fontSize={'36px'} fontWeight={700}>243</Text>
</Box>
<Box boxShadow={'lg'} color={"#fff"} p={3} rounded={'xl'} w={'25%'} display={'flex'} flexDirection={'column'} alignItems={'start'} gap={3} >
<Icon as={RiMoneyDollarBoxLine} boxSize={12} />
<Text as={'span'} fontSize={'xs'}>Total IO</Text>
<Text as={'span'} fontSize={'40px'} fontWeight={700}>243</Text>
</Box>
<Box boxShadow={'lg'} color={"#fff"} p={3} rounded={'xl'} w={'25%'} display={'flex'} flexDirection={'column'} alignItems={'start'} gap={3} >
<Icon as={RiMoneyDollarBoxLine} boxSize={12} />
<Text as={'span'} fontSize={'xs'}>Total sponors</Text>
<Text as={'span'} fontSize={'40px'} fontWeight={700}>243</Text>
</Box>
<Box boxShadow={'lg'} color={"#fff"} p={3} rounded={'xl'} w={'25%'} display={'flex'} flexDirection={'column'} alignItems={'start'} gap={3} >
<Icon as={RiMoneyDollarBoxLine} boxSize={12} />
<Text as={'span'} fontSize={'xs'}>Total Investment Type</Text>
<Text as={'span'} fontSize={'40px'} fontWeight={700}>243</Text>
</Box>
</Box>
</Box>
)
}
export default Dashbaord

View File

@@ -9,10 +9,11 @@ import {
Tooltip,
useToast,
useDisclosure,
Link,
} from "@chakra-ui/react";
import React, { useContext, useEffect, useState } from "react";
import { HiDotsVertical } from "react-icons/hi";
import { Link, Link as RouterLink, useNavigate } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import { debounce } from "../../Master/Sponser/AddSponser";
import { OPACITY_ON_LOAD } from "../../../Layout/animations";
import Pagination from "../../../Components/Pagination";
@@ -194,20 +195,10 @@ const DepositHistory = () => {
Status: (
<Box w={"70px"} isTruncated={true} cursor={"pointer"}>
<Text
// onClick={() => {
// setActionId(item.id);
// onConfirmOpen();
// }}
onClick={() => {
setActionId(item.id);
if (item.status === "Approved") {
onConfirmOpen();
} else {
onRejectOpen();
}
}}
as={"span"}
color={item.transactionStatus === "Approved" ? "green" : "red"}
color={item.transactionStatus === "Approved" ? "green.500" : "red.500"}
fontWeight={700}
>
{item.transactionStatus}
</Text>

View File

@@ -94,6 +94,9 @@ const schema = yup.object().shape({
const IODetails = ({ enableNextTab, index, data }) => {
const params = useParams();
@@ -171,6 +174,11 @@ const IODetails = ({ enableNextTab, index, data }) => {
const [values, setValues] = useState(id?minInvestmentById:miniValue);
const formatNumber = (num) => {
// Remove non-numeric characters and format with commas
return num.replace(/\D/g, '')
.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
};
// console.log(values);
// ======================[ Validator filter ]
@@ -593,8 +601,9 @@ const IODetails = ({ enableNextTab, index, data }) => {
};
return IObyIDisLoading ? (
<FullscreenLoaders />
<FullscreenLoaders height={'70vh'} />
) : (
<FormInputMain
p={0.1}
w={250}

View File

@@ -142,9 +142,9 @@ const IONAVDetails = () => {
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
{/* <HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} />
</HStack>
</HStack> */}
</HStack>
</Box>

View File

@@ -377,9 +377,9 @@ const Investors = () => {
onChange={(e) => setSearchTerm(e.target.value)}
/>
<HStack display={"flex"} alignItems={"center"}>
{/* <HStack display={"flex"} alignItems={"center"}>
<Pagination totalItems={10} />
</HStack>
</HStack> */}
</HStack>
</Box>

View File

@@ -77,7 +77,7 @@ const UpdateIOStatus = ({ isOpen, onClose }) => {
>
<Badge
textTransform={"none"}
variant={"solid"}
variant={"subtle"}
colorScheme={
selectedItem === "Draft"
? "blue"
@@ -99,7 +99,7 @@ const UpdateIOStatus = ({ isOpen, onClose }) => {
>
<Badge
textTransform={"none"}
variant={"solid"}
variant={"subtle"}
colorScheme={
statusAdmin === "Draft"
? "blue"
@@ -118,12 +118,13 @@ const UpdateIOStatus = ({ isOpen, onClose }) => {
</ModalBody>
<ModalFooter>
<Button
bg={"hsla(139, 100%, 14%, 1)"}
// bg={"hsla(139, 100%, 14%, 1)"}
colorScheme="forestGreen"
mr={3}
color={"#fff"}
_hover={{
bg: "hsl(139deg 98.99% 26.59%)",
}}
// _hover={{
// bg: "hsl(139deg 98.99% 26.59%)",
// }}
size={"sm"}
rounded={"sm"}
onClick={handleSubmit}

View File

@@ -299,7 +299,7 @@ const ViewIOdetails = () => {
}, {});
if (!IObyID?.data) {
return <FullscreenLoaders />;
return <FullscreenLoaders height={'70vh'} />;
}
return (

View File

@@ -7,19 +7,21 @@ import * as yup from "yup";
import { useNavigate, useParams } from "react-router-dom";
import FormInputMain from "../../../Components/FormInputMain";
import SwitchButton from "../../../Components/SwitchButton";
import {useCreateInvestmentTypeMutation,
import {
useCreateInvestmentTypeMutation,
useGetInvestmentTypeByIdQuery,
useUpdateInvestmentTypeMutation,
} from "../../../Services/investment.type.service";
import ToastBox from "../../../Components/ToastBox";
import CustomAlertDialog from "../../../Components/CustomAlertDialog";
import FullscreenLoaders from "../../../Components/Loaders/FullscreenLoaders";
// ======================= [validation] =========================
export const addInvestmentType = yup.object().shape({
investmentTypeName: yup.string()
.required('Investment type is required')
.max(50, 'Investment name cannot be more than 50 characters'),
.required('Investment type is required')
.max(50, 'Investment name cannot be more than 50 characters'),
note: yup.string().notRequired(),
investmentTypeNameArabic: yup.string().required(),
noteArabic: yup.string().notRequired(),
@@ -36,7 +38,7 @@ export function debounce(func, delay) {
}
const AddInvestmentType = () => {
const navigate = useNavigate();
const toast = useToast();
const params = useParams();
@@ -66,7 +68,7 @@ const AddInvestmentType = () => {
resolver: yupResolver(addInvestmentType),
});
const {data: investmentTypeByIdData,error,isLoading,} = useGetInvestmentTypeByIdQuery(id, { skip: !id });
const { data: investmentTypeByIdData, error, isLoading, } = useGetInvestmentTypeByIdQuery(id, { skip: !id });
@@ -88,7 +90,7 @@ const AddInvestmentType = () => {
return <FullscreenLoaders />;
}
// ============================ [API]===============================
// ============================ [API]===============================
const handleConfirm = async () => {
setIsLoadingBtn(true);
@@ -169,7 +171,7 @@ const AddInvestmentType = () => {
type: "text",
isRequired: true,
section: "Add Details",
maxLength:50
maxLength: 50
},
{
label: "Investment Type (Arabic) ",
@@ -179,7 +181,7 @@ const AddInvestmentType = () => {
isRequired: true,
section: "Add Details",
arabic: true,
maxLength:255
maxLength: 255
},
{
label: "Description (English)",
@@ -188,7 +190,7 @@ const AddInvestmentType = () => {
type: "textarea",
// isRequired: true,
section: "Add Details",
maxLength:255
maxLength: 255
},
{
label: "Description (Arabic)",
@@ -198,7 +200,7 @@ const AddInvestmentType = () => {
// isRequired: true,
arabic: true,
section: "Add Details",
maxLength:255
maxLength: 255
},
];
@@ -263,7 +265,7 @@ const AddInvestmentType = () => {
return groups;
}, {});
// ==================== [On Submit] ========================
const onSubmit = async (data) => {
@@ -275,37 +277,38 @@ const AddInvestmentType = () => {
};
return (
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={14}>
isLoading ? <FullscreenLoaders /> :
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={14}>
{/* ===================== [Switch Button] ======================== */}
{/* ===================== [Switch Button] ======================== */}
<Box mt={5} display={"flex"} justifyContent={"right"} mr={5}>
<SwitchButton isSwitchOn={isSwitchOn} setIsSwitchOn={setIsSwitchOn} />
</Box>
<Box mt={5} display={"flex"} justifyContent={"right"} mr={5}>
<SwitchButton isSwitchOn={isSwitchOn} setIsSwitchOn={setIsSwitchOn} />
</Box>
{/* ====================== [Form Input] ====================== */}
{/* ====================== [Form Input] ====================== */}
<FormInputMain
groupedFields={params?.id ? groupedEditFields : groupedFields}
control={control}
errors={errors}
onSubmit={handleSubmit(onSubmit)}
submitTitle={params?.id ? "Update" : "Submit"}
<FormInputMain
groupedFields={params?.id ? groupedEditFields : groupedFields}
control={control}
errors={errors}
onSubmit={handleSubmit(onSubmit)}
submitTitle={params?.id ? "Update" : "Submit"}
// btnLoading={isLoadingBtn}
>
</FormInputMain>
>
</FormInputMain>
{/* ======================= [Modal] =========================== */}
{/* ======================= [Modal] =========================== */}
<CustomAlertDialog
isOpen={alert}
onClose={() => setAlert(false)}
alertHandler={handleConfirm}
message={"Are you sure you want to add this?"}
isLoading={isLoadingBtn}
/>
</Box>
<CustomAlertDialog
isOpen={alert}
onClose={() => setAlert(false)}
alertHandler={handleConfirm}
message={"Are you sure you want to add this?"}
isLoading={isLoadingBtn}
/>
</Box>
);
};

View File

@@ -257,6 +257,7 @@ const AddSponser = () => {
};
return (
isLoading?<FullscreenLoaders/>:
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={14}>
{/* ===================== [Switch Button] ======================== */}

View File

@@ -13,7 +13,17 @@ const SplashScreen = () => {
gap={10}
>
<Image src={logo} />
<Spinner color='green.900' size='md' />
{/* <Spinner color='green.900' size='md' /> */}
<div className="dot-spinner">
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
<div className="dot-spinner__dot"></div>
</div>
</Box>
)
}

View File

@@ -32,12 +32,13 @@ import DepositRequest from "../Pages/Deposit/DepositRequest/DepositRequest";
import EditBankDetails from "../Pages/Admin/BankDetails/EditBankDetails";
import ExchangeHistory from "../Pages/Master/ExchangeRate/ExchangeHistroy";
import Welcome from "../Pages/Welcome";
import Dashbaord from "../Pages/Dashbaord";
export const RouteLink = [
// =============[ Tanami ]================
// ===============[ Management]===============
{ path: "/", Component: Welcome },
{ path: "/", Component: Dashbaord },
{ path: "/sponser", Component: Sponser },
{ path: "/sponser/add-sponser/:id", Component: AddSponser },
{ path: "/sponser/add-sponser", Component: AddSponser },

View File

@@ -1,16 +1,72 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
// Define a base query function with RTK Query
export const baseQuery = fetchBaseQuery({
baseUrl: 'https://sprint4.tanami.betadelivery.com/api/v1',
prepareHeaders: (headers) => {
const token = localStorage.getItem('accessToken');
if (token) {
headers.set('x-auth-token', `${token}`);
// 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) => {
let result = await 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;
},
})(args, api, extraOptions);
if (result.error && result.error.status === 401) {
// Handle token refresh
const refreshToken = localStorage.getItem('refreshToken');
if (refreshToken) {
try {
const refreshResult = await fetchBaseQuery({
baseUrl: 'https://sprint4.tanami.betadelivery.com/api/v1',
})({
url: '/refresh_token',
method: 'POST',
body: { refreshToken },
}, api, extraOptions);
if (refreshResult.data) {
// Save new tokens
localStorage.setItem('accessToken', refreshResult.data.access.token);
localStorage.setItem('refreshToken', refreshResult.data.refresh.token);
// Retry the original request with the new token
result = await 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;
},
})(args, api, extraOptions);
}
} catch (err) {
console.error('Failed to refresh token:', err);
// Handle refresh failure (e.g., redirect to login)
}
}
return headers;
},
});
}
return result;
};
// Create an RTK Query API slice
export const apiSlice = createApi({
@@ -27,7 +83,7 @@ export const apiSlice = createApi({
try {
const { data } = await queryFulfilled;
// Store tokens in local storage
localStorage.setItem('accessToken', data?.data?.access?.token);
localStorage.setItem('accessToken', data?.data?.access?.token) ;
localStorage.setItem('refreshToken', data?.data?.refresh?.token);
} catch (error) {
console.error('Login failed:', error);