Skip to content

Commit

Permalink
[finishes-187354198] user authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
Mag codes committed Jun 14, 2024
1 parent 4a2f100 commit 5d80884
Show file tree
Hide file tree
Showing 13 changed files with 166 additions and 87 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
"@hookform/resolvers": "^3.6.0",
"@reduxjs/toolkit": "^2.2.5",
"@types/react-redux": "^7.1.33",
"axios": "^1.7.2",
"clsx": "^2.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.51.5",
"react-icons": "^5.2.1",
"react-redux": "^9.1.2",
"react-router-dom": "^6.23.1",
"react-toastify": "^10.0.5",
"sass": "^1.77.2",
"tailwind-merge": "^2.3.0",
"zod": "^3.23.8"
Expand Down
20 changes: 0 additions & 20 deletions src/components/Counter.tsx

This file was deleted.

109 changes: 109 additions & 0 deletions src/components/LoginComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { useEffect } from 'react';
import { useForm, SubmitHandler } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import GoogleIcon from './../assets/googleIcon.svg';
import Button from './Button';
import Input from './Input';
import { loginSchema, LoginData } from './../utils/schemas';
import { useNavigate, useLocation } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { setToken, setUser } from '../redux/reducers/userSlice';
import { toast } from 'react-toastify';
import { useLoginUserMutation } from '../services/authenticationAPI';

Check failure on line 12 in src/components/LoginComponent.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Cannot find module '../services/authenticationAPI' or its corresponding type declarations.

const LoginComponent = () => {
const navigate = useNavigate();
const location = useLocation();
const dispatch = useDispatch();

const {
register,
handleSubmit,
setError,
formState: { errors, isSubmitting },
} = useForm<LoginData>({ resolver: zodResolver(loginSchema) });

const [loginUser, { data, error, isLoading, isSuccess, isError }] = useLoginUserMutation();

const onSubmit: SubmitHandler<LoginData> = async (userCredentials) => {
await loginUser(userCredentials);
};

useEffect(() => {
if (isError && error) {
setError('root', {
message: 'Invalid email or password',
});
return;
}
if (isSuccess && data) {
const token = data.token;

dispatch(setUser(data.user));
dispatch(setToken(token));

toast.success('Login successful');
const from = location.state?.from?.pathname || '/';
navigate(from);
}
}, [isError, isSuccess, error, data, navigate, location.state, dispatch]);

const handleGoogleAuthentication = (e: any) => {
e.preventDefault();
document.location.assign('https://e-commerce-mavericcks-bn-staging-istf.onrender.com/api/auth/google');
};

return (
<>
<div className='w-full xl:container flex flex-col-reverse gap-14 md:flex-row px-4 mb-8 items-center md:gap-5 transition-all md:justify-between md:px-10 lg:justify-around mt-5 md:mt-10'>
<div className='w-full md:w-7/12 xl:w-4/12 border-greenColor border-2 rounded-xl p-8'>
<p className='font-bold text-3xl'>Existing Customer?</p>
<p>Sign in to continue</p>
{errors.root && (
<p className='text-sm bg-redColor text-whiteColor mt-4py-2 rounded-lg px-3'>{errors.root.message}</p>
)}
<form onSubmit={handleSubmit(onSubmit)} className='flex flex-col gap-4 justify-center mt-8'>
<Input
id='loginEmail'
label='Email'
type='email'
placeholder='Enter your email'
{...register('email')}
error={errors.email && errors.email.message}
/>
<Input
id='loginPassword'
label='Password'
type='password'
placeholder='Enter your password'
{...register('password')}
error={errors.password && errors.password.message}
/>
<button
type='submit'
className='p-2 rounded-lg bg-greenColor hover:bg-darkGreen transition-all text-whiteColor font-bold'
>
{isSubmitting || isLoading ? 'Loading...' : 'Sign In'}
</button>
{isSuccess && <p className='text-green-600 text-xs font-normal'>Login successful!</p>}
<span className='self-center font-bold text-grayColor'>or</span>
<button
onClick={handleGoogleAuthentication}
className='p-2 font-bold text-teal-700 border-2 border-greenColor rounded-lg flex flex-row items-center justify-center gap-2 hover:bg-teal-100 transition-all'
>
<img src={GoogleIcon} alt='Google Icon' className='w-5' />
Continue with Google
</button>
</form>
</div>
<div className='w-full md:w-3/12 mt-0 p-5 border-b-2 md:p-0 md:border-b-0 h-fit flex flex-col gap-3 justify-self-start self-start'>
<p className='font-bold text-3xl'>New Customer?</p>
<p>Create an account here</p>
<Button type='button' text='Register Here' onClick={() => navigate('/register')} />
</div>
</div>
</>
);
};

export default LoginComponent;
4 changes: 2 additions & 2 deletions src/components/register/RegisterSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import { SubmitHandler, useForm } from 'react-hook-form';
import { extendedSchema, ExtendedFormFields } from '../../utils/schemas';
import { useNavigate } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import { useRegisterUserMutation } from '../../services/authAPI';
import { setUserRegistered } from '../../state/register/registerSlice';
import { setUserRegistered } from '../../redux/reducers/registerSlice';
import { useEffect } from 'react';
import { QueryErrorData } from '../../utils/schemas';
import { useRegisterUserMutation } from '../../services/authenticationAPI';

Check failure on line 13 in src/components/register/RegisterSection.tsx

View workflow job for this annotation

GitHub Actions / Build (20)

Cannot find module '../../services/authenticationAPI' or its corresponding type declarations.

const RegisterSection = () => {
const navigate = useNavigate();
Expand Down
1 change: 0 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
Expand Down
2 changes: 1 addition & 1 deletion src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './index.css';
import { store } from './state/store.ts';
import { store } from './redux/store.ts';
import { Provider } from 'react-redux';

ReactDOM.createRoot(document.getElementById('root')!).render(
Expand Down
25 changes: 10 additions & 15 deletions src/pages/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import { useNavigate } from "react-router-dom";
import LoginComponent from '../components/LoginComponent';
import Navbar from '../components/navbar/Navbar';
import Footer from '../components/footer/Footer';

function Login() {
const navigate = useNavigate();

const handleNavigate = () => {
navigate("/");
};

return (
<div className="w-screen h-screen flex flex-row items-start justify-center bg-slate-900 text-white pt-20">
<div className="flex flex-col gap-5">
<h1 className="text-5xl">Login page</h1>
<button className="border rounded-md p-3 hover:text-gray-800 hover:bg-gray-200" onClick={handleNavigate}>Back home</button>
</div>
</div>
);
return (
<>
<Navbar />
<LoginComponent />
<Footer />
</>
);
}

export default Login;
File renamed without changes.
31 changes: 31 additions & 0 deletions src/redux/reducers/userSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { createSlice } from '@reduxjs/toolkit';

const initialState = {
user: null,
token: null,
subscription: null,
};

const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
setUser: (state, action) => {
state.user = action.payload;
},
setToken: (state, action) => {
state.token = action.payload;
},
setSubscription: (state, action) => {
state.subscription = action.payload;
},
clearUserData: state => {
state.user = null;
state.token = null;
state.subscription = null;
},
},
});

export const { setUser, setToken, setSubscription, clearUserData } = userSlice.actions;
export default userSlice.reducer;
6 changes: 3 additions & 3 deletions src/state/store.ts → src/redux/store.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// src/store.js

import { configureStore } from '@reduxjs/toolkit';
import { mavericksApi } from '../services';
import counterReducer from './counter/counterSlice';
import { setupListeners } from '@reduxjs/toolkit/query';
import registerReducer from './register/registerSlice';
import registerReducer from './reducers/registerSlice';

export const store = configureStore({
reducer: {
counter: counterReducer,
register: registerReducer,
[mavericksApi.reducerPath]: mavericksApi.reducer,
},
Expand Down
4 changes: 2 additions & 2 deletions src/services/authAPI.ts → src/services/authenticationApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ const authAPI = mavericksApi.injectEndpoints({
}),
}),
loginUser: builder.mutation({
query: userData => ({
query: credentials => ({
url: `auth/login`,
method: 'POST',
body: userData,
body: credentials,
}),
}),
}),
Expand Down
43 changes: 0 additions & 43 deletions src/state/counter/counterSlice.ts

This file was deleted.

6 changes: 6 additions & 0 deletions src/utils/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,9 @@ export interface QueryErrorData {
status: number | undefined;
data: ErrorData;
}
export const loginSchema = z.object({
email: z.string().email({ message: 'A valid email is required' }),
password: z.string().min(5, { message: 'Password must be 5 characters or more' }),
});

export type LoginData = z.infer<typeof loginSchema>;

0 comments on commit 5d80884

Please sign in to comment.