updatw
This commit is contained in:
@@ -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 />
|
||||
|
||||
105
src/Components/Loaders/FullscreenLoaders.css
Normal file
105
src/Components/Loaders/FullscreenLoaders.css
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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
47
src/Pages/Dashbaord.jsx
Normal 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
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -299,7 +299,7 @@ const ViewIOdetails = () => {
|
||||
}, {});
|
||||
|
||||
if (!IObyID?.data) {
|
||||
return <FullscreenLoaders />;
|
||||
return <FullscreenLoaders height={'70vh'} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -257,6 +257,7 @@ const AddSponser = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
isLoading?<FullscreenLoaders/>:
|
||||
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={14}>
|
||||
|
||||
{/* ===================== [Switch Button] ======================== */}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user