Merge branch 'main' of http://git.wdipl.com/Siddhesh.More/SSA-Admin-Panel into yasin
This commit is contained in:
@@ -5,7 +5,7 @@ import GlobalStateContext from './GlobalStateContext';
|
||||
|
||||
|
||||
const GlobalStateProvider = ({ children }:{children:ReactNode}) => {
|
||||
const [isAuthenticate, setIsAuthenticate] = useState<boolean>(true);
|
||||
const [isAuthenticate, setIsAuthenticate] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<GlobalStateContext.Provider value={{ isAuthenticate, setIsAuthenticate }}>
|
||||
|
||||
@@ -3,32 +3,29 @@ import {
|
||||
createListCollection,
|
||||
Heading,
|
||||
HStack,
|
||||
Stack,
|
||||
Status,
|
||||
Tabs,
|
||||
Text,
|
||||
} from "@chakra-ui/react";
|
||||
import MainFrame from "../../components/MainFrame";
|
||||
import BarChart from "../../components/Charts/BarChart";
|
||||
import {
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectRoot,
|
||||
SelectTrigger,
|
||||
SelectValueText,
|
||||
} from "../../components/ui/select";
|
||||
import CircularApp from "../../components/Charts/CircularProgress";
|
||||
import SemiDoughnutChart from "../../components/Charts/SemiDoughnutChart";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import MainFrame from "../../components/MainFrame";
|
||||
import {
|
||||
AccordionItem,
|
||||
AccordionItemContent,
|
||||
AccordionItemTrigger,
|
||||
AccordionRoot,
|
||||
} from "../../components/ui/accordion";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import {
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectRoot,
|
||||
SelectTrigger,
|
||||
SelectValueText
|
||||
} from "../../components/ui/select";
|
||||
import AgencyName from "./AgencyName";
|
||||
import CircularProgress from "../../components/Charts/CircularProgress";
|
||||
import CircularApp from "../../components/Charts/CircularProgress";
|
||||
|
||||
const Dashboard = () => {
|
||||
const frameworks = createListCollection({
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
import { Center, HStack, Image, Input, Text, VStack } from "@chakra-ui/react"
|
||||
import axios from "axios"
|
||||
import { useContext, useState } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { useDispatch } from "react-redux"
|
||||
import GlobalStateContext from "../Contexts/GlobalStateContext"
|
||||
import { setToken } from "../Redux/Service/authSlice"
|
||||
import logo from '../assets/logo.svg'
|
||||
import { Button } from "../components/ui/button"
|
||||
import { Field } from "../components/ui/field"
|
||||
import { Toaster, toaster } from "../components/ui/toaster"
|
||||
import { Toaster } from "../components/ui/toaster"
|
||||
|
||||
interface FormValues {
|
||||
mobileNumber: number
|
||||
password: string
|
||||
}
|
||||
|
||||
const Login = () => {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false)
|
||||
const context = useContext(GlobalStateContext);
|
||||
if (!context) {
|
||||
@@ -26,24 +30,27 @@ const Login = () => {
|
||||
} = useForm<FormValues>()
|
||||
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
const onSubmit = handleSubmit(async (data) => {
|
||||
setIsLoading(true)
|
||||
if (data?.mobileNumber === 1234567890) {
|
||||
setTimeout(() => {
|
||||
setIsAuthenticate(true);
|
||||
setIsLoading(false)
|
||||
}, 3000); // 3-second delay
|
||||
try {
|
||||
const response = await axios.post(`${import.meta.env.VITE_API_URL}/v1/login`, {
|
||||
mobile_number: data.mobileNumber,
|
||||
password: data.password,
|
||||
});
|
||||
console.log('====================================');
|
||||
console.log(response);
|
||||
console.log('====================================');
|
||||
dispatch(setToken(String(response.data["access-token"])));
|
||||
|
||||
} else {
|
||||
toaster.create({
|
||||
title: `Invalid Credentials`,
|
||||
type: "error",
|
||||
})
|
||||
setIsLoading(false)
|
||||
} catch (error) {
|
||||
console.error("Login failed", error);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
|
||||
|
||||
@@ -79,7 +86,11 @@ const Login = () => {
|
||||
<Input ps={3} {...register("mobileNumber", { required: "Mobile Number address is required" })} placeholder="Mobile Number Address" />
|
||||
{/* <Text as={'span'} w={'100%'} fontSize={'xs'} fontWeight={'normal'} color={'#686677'}>Forget password</Text> */}
|
||||
</Field>
|
||||
<Button loading={isLoading} mt={4} size={'sm'} bg={'#02A0A0'} rounded={'md'} w={'100%'} color={'#ffffff'} type="submit">Send OTP</Button>
|
||||
<Field color={'#313039'} label={'Enter Mobile Number'} w={'100%'} invalid={!!errors.password} errorText={errors.password?.message} >
|
||||
<Input ps={3} {...register("password", { required: "Pasword is required" })} type="password" placeholder="Enter password" />
|
||||
{/* <Text as={'span'} w={'100%'} fontSize={'xs'} fontWeight={'normal'} color={'#686677'}>Forget password</Text> */}
|
||||
</Field>
|
||||
<Button loading={isLoading} mt={4} size={'sm'} bg={'#02A0A0'} rounded={'md'} w={'100%'} color={'#ffffff'} type="submit">Login</Button>
|
||||
|
||||
<Text>Forgot password</Text>
|
||||
</VStack>
|
||||
|
||||
55
src/Redux/Service/apiSlice.tsx
Normal file
55
src/Redux/Service/apiSlice.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
|
||||
import { BaseQueryFn, FetchArgs, FetchBaseQueryError } from "@reduxjs/toolkit/query";
|
||||
import { logout } from "./authSlice"; // Import logout action from authSlice
|
||||
import { RootState } from "../Store";
|
||||
|
||||
const baseQuery = fetchBaseQuery({
|
||||
baseUrl: "https://api.example.com",
|
||||
prepareHeaders: (headers, { getState }) => {
|
||||
const token = (getState() as RootState).auth.token; // Get token from Redux store
|
||||
if (token) {
|
||||
headers.set("Authorization", `Bearer ${token}`);
|
||||
}
|
||||
headers.set("Content-Type", "application/json");
|
||||
return headers;
|
||||
},
|
||||
});
|
||||
|
||||
// ✅ Handle 401 Errors (Auto Logout)
|
||||
const baseQueryWithReauth: BaseQueryFn<
|
||||
string | FetchArgs,
|
||||
unknown,
|
||||
FetchBaseQueryError
|
||||
> = async (args, api, extraOptions) => {
|
||||
const result = await baseQuery(args, api, extraOptions);
|
||||
|
||||
if (result.error && result.error.status === 401) {
|
||||
api.dispatch(logout()); // Logout user on 401 error
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const apiSlice = createApi({
|
||||
reducerPath: "api",
|
||||
baseQuery: baseQueryWithReauth, // Use enhanced baseQuery with error handling
|
||||
endpoints: (builder) => ({
|
||||
|
||||
|
||||
|
||||
getPosts: builder.query<Post[], void>({ query: () => "/posts" }),
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}),
|
||||
});
|
||||
|
||||
export const { useGetPostsQuery } = apiSlice;
|
||||
|
||||
export type Post = {
|
||||
id: number;
|
||||
title: string;
|
||||
body: string;
|
||||
};
|
||||
27
src/Redux/Service/authSlice.tsx
Normal file
27
src/Redux/Service/authSlice.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
type AuthState = {
|
||||
token: string | null;
|
||||
};
|
||||
|
||||
const initialState: AuthState = {
|
||||
token: localStorage.getItem("token"), // Load token from localStorage
|
||||
};
|
||||
|
||||
const authSlice = createSlice({
|
||||
name: "auth",
|
||||
initialState,
|
||||
reducers: {
|
||||
setToken: (state, action: PayloadAction<string>) => {
|
||||
state.token = action.payload;
|
||||
localStorage.setItem("token", action.payload);
|
||||
},
|
||||
logout: (state) => {
|
||||
state.token = null;
|
||||
localStorage.removeItem("token");
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setToken, logout } = authSlice.actions;
|
||||
export default authSlice.reducer;
|
||||
15
src/Redux/Store.tsx
Normal file
15
src/Redux/Store.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
import { apiSlice } from "./Service/apiSlice";
|
||||
import authReducer from "./Service/authSlice"
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
[apiSlice.reducerPath]: apiSlice.reducer,
|
||||
auth: authReducer,
|
||||
},
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware().concat(apiSlice.middleware),
|
||||
});
|
||||
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box, Text, VStack } from "@chakra-ui/react"
|
||||
import { Box, VStack } from "@chakra-ui/react"
|
||||
import { motion } from "framer-motion"
|
||||
import React, { FC } from "react"
|
||||
import { OPACITY_ON_LOAD } from "../Layouts/animations"
|
||||
@@ -11,7 +11,7 @@ interface MainFrameProps {
|
||||
title?: string
|
||||
}
|
||||
|
||||
const MainFrame: FC<MainFrameProps> = ({ children, title }) => {
|
||||
const MainFrame: FC<MainFrameProps> = ({ children }) => {
|
||||
return (
|
||||
<MotionVStack {...OPACITY_ON_LOAD} w="100%" h="90%" p={0} pb={0}>
|
||||
<Box
|
||||
|
||||
22
src/main.tsx
22
src/main.tsx
@@ -1,17 +1,23 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
import { Provider as ReduxProvider } from "react-redux";
|
||||
import { Provider } from './components/ui/provider'
|
||||
import GlobalStateProvider from './Contexts/GlobalStateProvider'
|
||||
import './index.css'
|
||||
import { Theme } from '@chakra-ui/react'
|
||||
import { store } from './Redux/Store'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
|
||||
<GlobalStateProvider>
|
||||
<Provider>
|
||||
<App />
|
||||
</Provider>
|
||||
</GlobalStateProvider>
|
||||
</React.StrictMode>,
|
||||
<React.StrictMode>
|
||||
<ReduxProvider store={store}> {/* ✅ Wrap with Redux Provider */}
|
||||
<GlobalStateProvider>
|
||||
<Provider> {/* ✅ Wrap with Provider */}
|
||||
<Theme appearance='light'>
|
||||
<App />
|
||||
</Theme>
|
||||
</Provider>
|
||||
</GlobalStateProvider>
|
||||
</ReduxProvider>
|
||||
</React.StrictMode>
|
||||
)
|
||||
Reference in New Issue
Block a user