Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User login and Signup with Google #19

Closed
wants to merge 17 commits into from
Closed
10,011 changes: 4,921 additions & 5,090 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"react-router-dom": "^6.24.0",
"react-spinners": "^0.14.1",
"react-toastify": "^10.0.5",
"sass": "^1.77.6",
"save-dev": "0.0.1-security",
"yup": "^1.4.0"
},
Expand Down
Binary file added public/email.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/failed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/not-found.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/sign-up.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion public/index.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="./images/logo.png" type="image/png">
<title>E-Commerce Ninjas FrontEnd</title>
</head>

<body>
<div id="root"></div>
</body>
</html>

</html>
Binary file added public/new-message.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/not-found.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/sign-up.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 61 additions & 0 deletions src/components/GoogleCallback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* eslint-disable */
import React, { useEffect, useState } from 'react';
import { useAppDispatch } from '../store/store';
import { googleAuthCallback } from '../store/features/auth/authSlice';
import { useNavigate } from 'react-router-dom';
import { PuffLoader } from 'react-spinners';
import "../../src/styles/Loader.scss";

const GoogleCallback = () => {
const dispatch = useAppDispatch();
const navigate = useNavigate();
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const scope = urlParams.get('scope');
const authuser = urlParams.get('authuser');
const prompt = urlParams.get('prompt');

const authenticate = async () => {
try {
if (code && scope) {
const response = await dispatch(
googleAuthCallback({ code, scope, authuser, prompt })
);
const token = response.payload.data.token;
localStorage.setItem('token', token);
navigate('/');
} else {
console.error('Missing authentication parameters');
setError('Missing authentication parameters');
}
} catch (error) {
console.error('Authentication failed', error);
setError('Authentication failed. Please try again.');
} finally {
setLoading(false);
}
};

authenticate();
}, [dispatch, navigate]);

if (loading) {
return (
<div className="loader">
<PuffLoader color="#ff6d18" size={300} loading={loading} />
</div>
);
}

if (error) {
return <div className="error-message">{error}</div>;
}

return <div>Authenticating....</div>;
};

export default GoogleCallback;
30 changes: 15 additions & 15 deletions src/components/Meta.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
/* eslint-disable */
import React from 'react'
import {Helmet} from 'react-helmet'
interface MetaProps {
title: string;
}
export const Meta: React.FC<MetaProps> = (props) => {
return (
<Helmet>
<meta charSet="utf-8" />
<title>{props.title}</title>
</Helmet>
);
/* eslint-disable */
import React from 'react'
import {Helmet} from 'react-helmet'

interface MetaProps {
title: string;
}

export const Meta: React.FC<MetaProps> = (props) => {
return (
<Helmet>
<meta charSet="utf-8" />
<title>{props.title}</title>
</Helmet>
);
};
2 changes: 1 addition & 1 deletion src/components/ResendEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,4 @@ export const ResendEmail = () => {
</div>
</>
)
}
}
30 changes: 15 additions & 15 deletions src/components/layout/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/* eslint-disable */
import React from 'react';
import Header from './Header';
import { Outlet } from 'react-router-dom';
import Footer from './Footer';
export default function Layout() {
return (
<>
<Header />
<Outlet />
<Footer />
</>
);
}
/* eslint-disable */
import React from 'react';
import Header from './Header';
import { Outlet } from 'react-router-dom';
import Footer from './Footer';

export default function Layout() {
return (
<>
<Header />
<Outlet />
<Footer />
</>
);
}
2 changes: 1 addition & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ root.render(
<App />
<ToastContainer />
</Provider>
);
);
2 changes: 1 addition & 1 deletion src/pages/EmailVerifying.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ export const EmailVerifying = () => {
</div>
</>
)
}
}
2 changes: 1 addition & 1 deletion src/pages/LandingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ const LandingPage: React.FC = () => {
);
};

export default LandingPage;
export default LandingPage;
2 changes: 1 addition & 1 deletion src/pages/NotFound.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ const NotFound: React.FC = () => (
</main>
);

export default NotFound;
export default NotFound;
4 changes: 2 additions & 2 deletions src/pages/SignUp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { registerUser } from '../store/features/auth/authSlice';
import { CircleLoader, PuffLoader } from 'react-spinners';
import SignUpIcon from '../../public/assets/images/sign-up.png'
import { toast } from 'react-toastify';
import authService from '../store/features/auth/authService';
const SignUpSchema = Yup.object().shape({
email: Yup.string().email('Email must be valid').required('Email is required'),
password: Yup.string().required('Password is required'),
Expand Down Expand Up @@ -121,7 +122,7 @@ export const SignUp = () => {
<p>or Sign up with</p>
</div>
<div>
<div className='google'>
<div className='google' onClick={authService.googleAuth}>
<FcGoogle className='google-icon' />
<p>Continue with google</p>
</div>
Expand All @@ -134,4 +135,3 @@ export const SignUp = () => {
</>
)
}

2 changes: 1 addition & 1 deletion src/pages/VerifyEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ const VerifyEmail: React.FC = () => {
);
};

export default VerifyEmail;
export default VerifyEmail;
4 changes: 3 additions & 1 deletion src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Layout from './components/layout/Layout';
import VerifyEmail from './pages/VerifyEmail';
import { EmailVerifying } from './pages/EmailVerifying';
import { ResendEmail } from './components/ResendEmail';
import GoogleCallback from './components/GoogleCallback';



Expand All @@ -21,11 +22,12 @@ const AppRouter: React.FC = () => {
<Route path="verify-email" element={<EmailVerifying />} />
<Route path="resend-email" element={<ResendEmail />} />
<Route path="/api/auth/verify-email/:token" element={<VerifyEmail />} />
<Route path="/api/auth/google/callback" element={<GoogleCallback />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
</div>
);
};

export default AppRouter;
export default AppRouter;
60 changes: 40 additions & 20 deletions src/store/features/auth/authService.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,47 @@
/* eslint-disable */
import {axiosInstance} from "../../../utils/axios/axiosInstance";
import { IEmail, IUser, IVerification } from "../../../utils/types/store";
import { axiosInstance } from '../../../utils/axios/axiosInstance';
import { IEmail, IUser, IVerification } from '../../../utils/types/store';
import { URL } from '../../../utils/axios/axiosInstance';

const register = async(userData: IUser) =>{
const response = await axiosInstance.post<IUser>('/api/auth/register', userData);
return response.data;
}
const register = async (userData: IUser) => {
const response = await axiosInstance.post<IUser>(
'/api/auth/register',
userData
);
return response.data;
};

const verify = async(token:string) =>{
const response = await axiosInstance.get<IVerification>(`/api/auth/verify-email/${token}`);
return response.data;
}
const verify = async (token: string) => {
const response = await axiosInstance.get<IVerification>(
`/api/auth/verify-email/${token}`
);
return response.data;
};

const resendVerificationEmail = async(email:IEmail) => {
const response = await axiosInstance.post<IEmail>(`/api/auth/send-verify-email`, email);
return response.data;
}
const resendVerificationEmail = async (email: IEmail) => {
const response = await axiosInstance.post<IEmail>(
`/api/auth/send-verify-email`,
email
);
return response.data;
};
const googleAuth = async () => {
window.location.href = `${URL}/api/auth/google`;
};

const authService = {
register,
verify,
resendVerificationEmail,
const googleAuthCallback = async (data: any) => {
const response = await axiosInstance.get(
`/api/auth/google/callback?code=${data.code}&scope=${data.scope}&authuser=${data.authuser}&prompt=${data.prompt}`
);
return response.data;
};

}
const authService = {
register,
verify,
resendVerificationEmail,
googleAuth,
googleAuthCallback,
};

export default authService;
export default authService;
51 changes: 50 additions & 1 deletion src/store/features/auth/authSlice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,27 @@ export const resendVerificationEmail = createAsyncThunk("auth/resend-verificatio
}
})



export const googleAuth = createAsyncThunk("auth/google", async(_,thunkApi) => {
try {
const response = await authService.googleAuth();
return response;
} catch (error) {
return thunkApi.rejectWithValue(getErrorMessage(error));
}
})

export const googleAuthCallback = createAsyncThunk('auth/googleAuthCallback', async(data:any, thunkApi) => {
try {
const response = await authService.googleAuthCallback(data);
return response;
} catch (error) {
return thunkApi.rejectWithValue(getErrorMessage(error));
}
})


const userSlice = createSlice({
name: 'auth',
initialState,
Expand Down Expand Up @@ -94,7 +115,35 @@ const userSlice = createSlice({
state.isError = true;
state.message = action.payload;
})
.addCase(googleAuth.pending, (state) => {
state.isLoading = true;

})
.addCase(googleAuth.fulfilled, (state, action: PayloadAction<any>) => {
state.isLoading = false;
state.isAuthenticated = true;
})
.addCase(googleAuth.rejected, (state, action: PayloadAction<any>) => {
state.isLoading = false;
state.isError = true;
})
.addCase(googleAuthCallback.pending, (state) => {
state.isLoading = true;
state.isError = false;
state.isSuccess = false;
})
.addCase(googleAuthCallback.fulfilled, (state, action: PayloadAction<any>) => {
state.isLoading = false;
state.isSuccess = true;
state.user = action.payload;
state.message = action.payload.message;
})
.addCase(googleAuthCallback.rejected, (state, action: PayloadAction<any>) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
},
});

export default userSlice.reducer;
export default userSlice.reducer;
2 changes: 1 addition & 1 deletion src/store/features/welcomeSlice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ export const WelcomeSlice = createSlice({
},
});

export default WelcomeSlice.reducer;
export default WelcomeSlice.reducer;
2 changes: 1 addition & 1 deletion src/stories/Button.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ export const ShortTitle: Story = {
title: 'Short Btn',
type: 'button',
},
};
};
7 changes: 7 additions & 0 deletions src/styles/Loader.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.loader {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}

Loading
Loading