diff --git a/package-lock.json b/package-lock.json
index 7c2f664..99943fb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,6 +17,7 @@
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.2",
"formik": "^2.4.5",
+ "prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^4.12.0",
diff --git a/package.json b/package.json
index 14607a7..1f87f93 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.2",
"formik": "^2.4.5",
+ "prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^4.12.0",
diff --git a/src/components/Button/FormSubmitButton.js b/src/components/Button/FormSubmitButton.js
index bbf6f93..e72d934 100644
--- a/src/components/Button/FormSubmitButton.js
+++ b/src/components/Button/FormSubmitButton.js
@@ -1,11 +1,11 @@
import styled from '@emotion/styled';
-export const FormSubmitButton = styled.button`
+const FormSubmitButton = styled.button`
padding: 0.56rem 5.06rem;
font-weight: 400;
- color: #FFFFFF;
+ color: #000;
margin-top: 10px;
- border-radius: 0.25rem;
+ border-radius: 0.9rem;
border: 1px solid transparent;
font-size: 1.125rem;
height: fit-content;
@@ -47,3 +47,5 @@ export const FormSubmitButton = styled.button`
width: fit-content;
}
`;
+
+export default FormSubmitButton;
diff --git a/src/components/Button/TertiaryButton.js b/src/components/Button/TertiaryButton.js
index a180670..ae978e3 100644
--- a/src/components/Button/TertiaryButton.js
+++ b/src/components/Button/TertiaryButton.js
@@ -1,9 +1,8 @@
+import PropTypes from 'prop-types';
import React from 'react';
import styled from '@emotion/styled';
-export const TertiaryButton = ({ children }) => {
- return {children};
-};
+const TertiaryButton = ({ children }) => {children};
const StyledButton = styled.button`
display: flex;
@@ -14,3 +13,9 @@ const StyledButton = styled.button`
background-color: transparent;
cursor: pointer;
`;
+
+TertiaryButton.propTypes = {
+ children: PropTypes.node.isRequired,
+};
+
+export default TertiaryButton;
diff --git a/src/components/Form/FormComponent.js b/src/components/Form/FormComponent.js
index f45664f..33a02f6 100644
--- a/src/components/Form/FormComponent.js
+++ b/src/components/Form/FormComponent.js
@@ -1,32 +1,48 @@
+import PropTypes from 'prop-types';
import { Form, Formik } from 'formik';
import styled from '@emotion/styled';
-
-export const FormComponent = ({
+const FormComponent = ({
initialValues,
schema,
onSubmit,
children,
className,
-}) => {
- return (
-
-
- {children}
-
-
- );
+}) => (
+
+
+ {children}
+
+
+);
+
+FormComponent.propTypes = {
+ initialValues: PropTypes.shape({
+ userName: PropTypes.string.isRequired,
+ email: PropTypes.string.isRequired,
+ password: PropTypes.string.isRequired,
+ confirm_password: PropTypes.string.isRequired,
+ }).isRequired,
+ schema: PropTypes.shape({
+ userName: PropTypes.string.isRequired,
+ email: PropTypes.string.isRequired,
+ password: PropTypes.string.isRequired,
+ confirm_password: PropTypes.string.isRequired,
+ }).isRequired,
+ onSubmit: PropTypes.func.isRequired,
+ children: PropTypes.node.isRequired,
+ className: PropTypes.string.isRequired,
};
const StyledForm = styled(Form)`
border: 1px solid orange;
&.flex {
- dispaly: flex;
+ display: flex;
width: 60%;
}
@@ -35,7 +51,7 @@ const StyledForm = styled(Form)`
}
& > .margin {
- gird-colum: span 2;
+ gird-column: span 2;
margin-left: 15%;
margin-right: 15%;
}
@@ -45,3 +61,5 @@ const StyledForm = styled(Form)`
align-items: last baseline;
}
`;
+
+export default FormComponent;
diff --git a/src/components/Form/FormField.js b/src/components/Form/FormField.js
index 29b89eb..c60c714 100644
--- a/src/components/Form/FormField.js
+++ b/src/components/Form/FormField.js
@@ -1,9 +1,12 @@
-import { useField } from "formik";
-import React from 'react'
+import { useField } from 'formik';
+import React from 'react';
+import PropTypes from 'prop-types';
-import styled from "@emotion/styled";
+import styled from '@emotion/styled';
-export const SelectField = ({label, lpiSrc, rpiSrc, className, name, id, ...props}) => {
+export const SelectField = ({
+ label, lpiSrc, rpiSrc, className, name, id, ...props
+}) => {
const [field, meta] = useField(name);
return (
@@ -19,19 +22,30 @@ export const SelectField = ({label, lpiSrc, rpiSrc, className, name, id, ...prop
{meta.error}
) : null}
- )
-}
+ );
+};
-export const TextInputField = ({label, lpiSrc, rpiSrc, className, name, id, ...props}) => {
+SelectField.propTypes = {
+ label: PropTypes.string.isRequired,
+ lpiSrc: PropTypes.string.isRequired,
+ rpiSrc: PropTypes.string.isRequired,
+ className: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ id: PropTypes.string.isRequired,
+};
+
+export const TextInputField = ({
+ label, lpiSrc, rpiSrc, className, name, placeholder, id, ...props
+}) => {
const [field, meta] = useField(name);
- return(
+ return (
{label}
-
+
{lpiSrc ? : null}
{lpiSrc ? : null}
@@ -39,39 +53,58 @@ export const TextInputField = ({label, lpiSrc, rpiSrc, className, name, id, ...p
{meta.error}
) : null}
- )
-}
+ );
+};
+
+TextInputField.propTypes = {
+ label: PropTypes.string.isRequired,
+ lpiSrc: PropTypes.string.isRequired,
+ rpiSrc: PropTypes.string.isRequired,
+ className: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ placeholder: PropTypes.string.isRequired,
+ id: PropTypes.string.isRequired,
+};
export const TextAreaInputField = ({
- label,
- lpiSrc,
- rpiSrc,
- className,
- name,
- id,
- ...props
- }) => {
- const [field, meta] = useField(name);
-
- return (
-
- {label}
-
-
- {lpiSrc ? : null}
- {rpiSrc ? : null}
-
- {meta.touched && meta.error ? (
- {meta.error}
- ) : null}
-
- );
- };
+ label,
+ lpiSrc,
+ rpiSrc,
+ className,
+ name,
+ id,
+ ...props
+}) => {
+ const [field, meta] = useField(name);
+
+ return (
+
+ {label}
+
+
+ {lpiSrc ? : null}
+ {rpiSrc ? : null}
+
+ {meta.touched && meta.error ? (
+ {meta.error}
+ ) : null}
+
+ );
+};
+
+TextAreaInputField.propTypes = {
+ label: PropTypes.string.isRequired,
+ lpiSrc: PropTypes.string.isRequired,
+ rpiSrc: PropTypes.string.isRequired,
+ className: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ id: PropTypes.string.isRequired,
+};
export const InputWrapper = styled.div`
width: 100%;
diff --git a/src/components/Form/HideableTextFormField.js b/src/components/Form/HideableTextFormField.js
index 650d906..424627e 100644
--- a/src/components/Form/HideableTextFormField.js
+++ b/src/components/Form/HideableTextFormField.js
@@ -1,5 +1,6 @@
import { useField } from 'formik';
import { useState } from 'react';
+import PropTypes from 'prop-types';
import styled from '@emotion/styled';
import VisibilityIcon from '@mui/icons-material/Visibility';
@@ -7,14 +8,13 @@ import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import {
FieldErrorInfo,
- IInputField,
Input,
InputContainer,
InputLabel,
InputWrapper,
} from './FormField';
-export const HideableTextFormField = ({
+const HideableTextFormField = ({
label,
lpiSrc,
rpiSrc,
@@ -24,9 +24,8 @@ export const HideableTextFormField = ({
// apiKey,
...props
}) => {
- const [field, meta] = useField(props);
+ const [field, meta] = useField(name);
const [showKey, setShowKey] = useState(false);
-
const handleToggle = () => {
setShowKey(!showKey);
};
@@ -59,9 +58,21 @@ export const HideableTextFormField = ({
);
};
+HideableTextFormField.propTypes = {
+ label: PropTypes.string.isRequired,
+ lpiSrc: PropTypes.string.isRequired,
+ rpiSrc: PropTypes.string.isRequired,
+ className: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ id: PropTypes.string.isRequired,
+ // apiKey: PropTypes.string,
+};
+
const VisibiltyToggleIconsArea = styled.div`
display: flex;
margin-right: 1rem;
margin-left: auto;
margin-top: 0.75rem;
-`;
\ No newline at end of file
+`;
+
+export default HideableTextFormField;
diff --git a/src/components/Img/IconImg.js b/src/components/Img/IconImg.js
index ce100c9..e1c1bab 100644
--- a/src/components/Img/IconImg.js
+++ b/src/components/Img/IconImg.js
@@ -1,15 +1,12 @@
+import PropTypes from 'prop-types';
import React from 'react';
import styled from '@emotion/styled';
-const IconImg = ({ src }) => {
- return (
-
-
-
- );
-};
-
-export default IconImg;
+const IconImg = ({ src }) => (
+
+
+
+);
const ImageContainer = styled.div`
/* border: 2px solid red; */
@@ -23,3 +20,9 @@ const Img = styled.img`
object-fit: cover;
object-position: center;
`;
+
+IconImg.propTypes = {
+ src: PropTypes.string.isRequired,
+};
+
+export default IconImg;
diff --git a/src/components/Img/Img.js b/src/components/Img/Img.js
index 366108c..c27ac09 100644
--- a/src/components/Img/Img.js
+++ b/src/components/Img/Img.js
@@ -1,12 +1,17 @@
+import PropTypes from 'prop-types';
import styled from '@emotion/styled';
import React from 'react';
-const Img = ({ src, alt, className }) => {
- return ;
-};
-
-export default Img;
+const Img = ({ src, alt, className }) => ;
const Imgg = styled.img`
width: 100%;
`;
+
+Img.propTypes = {
+ src: PropTypes.string.isRequired,
+ alt: PropTypes.string.isRequired,
+ className: PropTypes.string.isRequired,
+};
+
+export default Img;
diff --git a/src/components/Link/Link.js b/src/components/Link/Link.js
index 7e20217..f7075f0 100644
--- a/src/components/Link/Link.js
+++ b/src/components/Link/Link.js
@@ -1,3 +1,5 @@
+import PropTypes from 'prop-types';
+
import { NavLink as RouterLink } from 'react-router-dom';
import styled from '@emotion/styled';
@@ -18,17 +20,15 @@ export const ButtonLink = styled(RouterLink)`
font-weight: 500;
`;
-export const NavBoxItem = ({ icon, path, children }) => {
- return (
- (navData.isActive ? 'active' : '')}
- to={path}
- >
- {icon}
- {children}
-
- );
-};
+export const NavBoxItem = ({ icon, path, children }) => (
+ (navData.isActive ? 'active' : '')}
+ to={path}
+ >
+ {icon}
+ {children}
+
+);
export const NavigationBoxLink = styled(RouterLink)`
text-decoration: none;
@@ -52,3 +52,9 @@ export const NavigationBoxLink = styled(RouterLink)`
color: blue;
}
`;
+
+NavBoxItem.propTypes = {
+ icon: PropTypes.node.isRequired,
+ path: PropTypes.string.isRequired,
+ children: PropTypes.node.isRequired,
+};
diff --git a/src/index.js b/src/index.js
index 74a1f71..8d2f138 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,13 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
+import { Provider } from 'react-redux';
import App from './App';
+import store from './redux/store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
-
-
-
+
+
+
+
+
,
);
diff --git a/src/layout/Auth/AuthLayout.js b/src/layout/Auth/AuthLayout.js
index 2caa72d..b718d5e 100644
--- a/src/layout/Auth/AuthLayout.js
+++ b/src/layout/Auth/AuthLayout.js
@@ -1,12 +1,10 @@
-import React from "react";
-import { Outlet } from "react-router-dom";
+import React from 'react';
+import { Outlet } from 'react-router-dom';
-const AuthLayout = () => {
- return (
-
-
-
- );
-};
+const AuthLayout = () => (
+
+
+
+);
-export default AuthLayout;
\ No newline at end of file
+export default AuthLayout;
diff --git a/src/layout/LandingPage/LandingPageLayout.js b/src/layout/LandingPage/LandingPageLayout.js
index 1b92bc8..7635e5b 100644
--- a/src/layout/LandingPage/LandingPageLayout.js
+++ b/src/layout/LandingPage/LandingPageLayout.js
@@ -2,18 +2,16 @@ import React from 'react';
import { Outlet } from 'react-router-dom';
import Footer from './footer/Footer';
-import { Header } from './header/Header';
+import Header from './header/Header';
-const LandingPageLayout = () => {
- return (
- <>
-
-
-
-
-
- >
- );
-};
+const LandingPageLayout = () => (
+ <>
+
+
+
+
+
+ >
+);
export default LandingPageLayout;
diff --git a/src/layout/LandingPage/footer/Footer.js b/src/layout/LandingPage/footer/Footer.js
index 03cc178..c02e35b 100644
--- a/src/layout/LandingPage/footer/Footer.js
+++ b/src/layout/LandingPage/footer/Footer.js
@@ -1,17 +1,15 @@
import React from 'react';
import styled from '@emotion/styled';
-const Footer = () => {
- return (
-
-
- This is the footer
-
-
- )
-}
+const Footer = () => (
+
+
+ This is the footer
+
+
+);
-export default Footer
+export default Footer;
const Container = styled.div`
border: 2px solid red;
diff --git a/src/layout/LandingPage/header/HamburgerMenu.js b/src/layout/LandingPage/header/HamburgerMenu.js
index db66bf3..696e4e7 100644
--- a/src/layout/LandingPage/header/HamburgerMenu.js
+++ b/src/layout/LandingPage/header/HamburgerMenu.js
@@ -1,16 +1,21 @@
-import React from "react";
-import styled from "@emotion/styled";
-import { FaBars } from 'react-icons/fa'
+import PropTypes from 'prop-types';
+import React from 'react';
+import styled from '@emotion/styled';
+import { FaBars } from 'react-icons/fa';
-const HamburgerMenu = ({onClick}) => {
- return
+const HamburgerMenu = ({ onClick }) => (
+
-}
+);
const StyledHamburger = styled.div`
font-size: 24px;
cursor: pointer;
`;
-export default HamburgerMenu;
\ No newline at end of file
+HamburgerMenu.propTypes = {
+ onClick: PropTypes.func.isRequired,
+};
+
+export default HamburgerMenu;
diff --git a/src/layout/LandingPage/header/Header.js b/src/layout/LandingPage/header/Header.js
index 5d1885a..cd08b1b 100644
--- a/src/layout/LandingPage/header/Header.js
+++ b/src/layout/LandingPage/header/Header.js
@@ -1,20 +1,19 @@
-import React, {useState, useRef, useEffect} from "react";
-import styled from "@emotion/styled";
+// import React, { useState, useRef, useEffect } from 'react';
+import React from 'react';
+import styled from '@emotion/styled';
-import HamburgerMenu from "./HamburgerMenu";
-import Nav from "../nav/Nav";
-import AuthNav from "../nav/AuthNav";
+// import HamburgerMenu from './HamburgerMenu';
+import Nav from '../nav/Nav';
+import AuthNav from '../nav/AuthNav';
-export const Header = () => {
- return (
-
-
-
-
-
-
- )
-}
+const Header = () => (
+
+
+
+
+
+
+);
const StyledHeader = styled.header`
display: flex;
@@ -35,4 +34,6 @@ const NavContainer = styled.header`
gap: 14rem;
right: 8rem;
background-color: white;
-`;
\ No newline at end of file
+`;
+
+export default Header;
diff --git a/src/layout/LandingPage/nav/AuthNav.js b/src/layout/LandingPage/nav/AuthNav.js
index f3d5648..7f13a9d 100644
--- a/src/layout/LandingPage/nav/AuthNav.js
+++ b/src/layout/LandingPage/nav/AuthNav.js
@@ -2,23 +2,21 @@ import React from 'react';
import styled from '@emotion/styled';
-import { TertiaryButton } from '../../../components/Button/TertiaryButton';
+import TertiaryButton from '../../../components/Button/TertiaryButton';
import { ButtonLink } from '../../../components/Link/Link';
import { ACCOUNT, SIGNIN, SIGNUP } from '../../../routes/routeConstants';
-const AuthNav = () => {
- return (
-
-
- Sign In
-
+const AuthNav = () => (
+
+
+ Sign In
+
-
- Sign Up
-
-
- );
-};
+
+ Sign Up
+
+
+);
const Container = styled.div`
/* border: 2px solid green; */
diff --git a/src/models/signin.model.js b/src/models/signin.model.js
new file mode 100644
index 0000000..93bcc18
--- /dev/null
+++ b/src/models/signin.model.js
@@ -0,0 +1,11 @@
+import * as Yup from 'yup';
+
+export const SignInSchema = Yup.object().shape({
+ email: Yup.string().email('Invalid email address').required('Email is required'),
+ password: Yup.string().required('Password is required'),
+});
+
+export const signInInitialValues = {
+ email: '',
+ password: '',
+};
diff --git a/src/models/signup.model.js b/src/models/signup.model.js
index 97a31ad..625f351 100644
--- a/src/models/signup.model.js
+++ b/src/models/signup.model.js
@@ -1,15 +1,15 @@
import * as Yup from 'yup';
export const SignUpSchema = Yup.object().shape({
- firstName: Yup.string().required('First name is required'),
- lastName: Yup.string().required('Last name is required'),
+ userName: Yup.string().required('User Name is required'),
email: Yup.string().email('Invalid email address').required('Email is required'),
password: Yup.string().min(6, 'Password must be at least 6 characters').required('Password is required'),
+ confirm_password: Yup.string().oneOf([Yup.ref('password'), null], 'Password must match').required('Confirm the Password'),
});
export const signUpInitialValues = {
- firstName: '',
- lastName: '',
+ userName: '',
email: '',
password: '',
-}
\ No newline at end of file
+ confirm_password: '',
+};
diff --git a/src/pages/Auth/SignIn/SignIn.js b/src/pages/Auth/SignIn/SignIn.js
index f82f1cc..378e442 100644
--- a/src/pages/Auth/SignIn/SignIn.js
+++ b/src/pages/Auth/SignIn/SignIn.js
@@ -1,5 +1,11 @@
import React from 'react';
+import SignInForm from './SigninForm';
-const SignIn = () => SignIn Page
;
+const SignIn = () => (
+
+
Login Page
+
+
+);
export default SignIn;
diff --git a/src/pages/Auth/SignIn/SigninForm.js b/src/pages/Auth/SignIn/SigninForm.js
index e69de29..fbee6d3 100644
--- a/src/pages/Auth/SignIn/SigninForm.js
+++ b/src/pages/Auth/SignIn/SigninForm.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import { useDispatch } from 'react-redux';
+import FormComponent from '../../../components/Form/FormComponent';
+import { SignInSchema, signInInitialValues } from '../../../models/signin.model';
+import FormSubmitButton from '../../../components/Button/FormSubmitButton';
+import { TextInputField } from '../../../components/Form/FormField';
+// import { useEffect } from 'react';
+import { userLogin } from '../../../redux/thunk';
+// import { useSelector } from 'react-redux';
+import HideableTextFormField from '../../../components/Form/HideableTextFormField';
+
+const SignInForm = () => {
+ const dispatch = useDispatch();
+ // const authenticatedUser = (state => state.authentication.authenticatedUser)
+ const handleSubmit = (values) => {
+ console.log(values);
+ dispatch(userLogin(values));
+ };
+
+ return (
+
+
+
+
+ Log In
+
+
+ );
+};
+
+export default SignInForm;
diff --git a/src/pages/Auth/SignUp/SignUp.js b/src/pages/Auth/SignUp/SignUp.js
index 3ee9078..28ec396 100644
--- a/src/pages/Auth/SignUp/SignUp.js
+++ b/src/pages/Auth/SignUp/SignUp.js
@@ -1,5 +1,11 @@
import React from 'react';
+import SignUpForm from './SignupForm';
-const SignUp = () => SignUp Page
;
+const SignUp = () => (
+
+
Signup page
+
+
+);
export default SignUp;
diff --git a/src/pages/Auth/SignUp/SignupForm.js b/src/pages/Auth/SignUp/SignupForm.js
index e69de29..53308a4 100644
--- a/src/pages/Auth/SignUp/SignupForm.js
+++ b/src/pages/Auth/SignUp/SignupForm.js
@@ -0,0 +1,37 @@
+import React from 'react';
+import { useDispatch } from 'react-redux';
+import FormComponent from '../../../components/Form/FormComponent';
+import { SignUpSchema, signUpInitialValues } from '../../../models/signup.model';
+import FormSubmitButton from '../../../components/Button/FormSubmitButton';
+import { TextInputField } from '../../../components/Form/FormField';
+// import { useEffect } from 'react';
+import { userSignUp } from '../../../redux/thunk';
+import HideableTextFormField from '../../../components/Form/HideableTextFormField';
+
+const SignUpForm = () => {
+ const dispatch = useDispatch();
+
+ const handleSubmit = (values) => {
+ console.log(values);
+ dispatch(userSignUp(values));
+ };
+
+ return (
+
+
+
+
+
+
+ Sign Up
+
+
+ );
+};
+
+export default SignUpForm;
diff --git a/src/pages/NotFound404.js b/src/pages/NotFound404.js
index f9f94e2..41e4f4b 100644
--- a/src/pages/NotFound404.js
+++ b/src/pages/NotFound404.js
@@ -1,9 +1,7 @@
import React from 'react';
-const NotFound404 = () => {
- return (
- NotFound404
- );
-};
+const NotFound404 = () => (
+ NotFound404
+);
export default NotFound404;
diff --git a/src/redux/authentication/authenticationSlice.js b/src/redux/authentication/authenticationSlice.js
new file mode 100644
index 0000000..24364f0
--- /dev/null
+++ b/src/redux/authentication/authenticationSlice.js
@@ -0,0 +1,64 @@
+import { createSlice } from '@reduxjs/toolkit';
+import {
+ userSignUp, userLogin, userLogout, fetchUser,
+} from '../thunk';
+
+const initialState = {
+ authenticatedUser: {},
+ status: 'idle',
+ error: null,
+};
+
+const authenticationSlice = createSlice({
+ name: 'authenticatedUser',
+ initialState,
+ extraReducers: (builder) => {
+ builder
+ .addCase(userSignUp.pending, (state) => {
+ state.status = 'loading';
+ })
+ .addCase(userSignUp.fulfilled, (state, action) => {
+ state.authenticatedUser = action.payload.data;
+ state.status = action.payload.status === 200 ? 'succeeded' : 'failed';
+ })
+ .addCase(userSignUp.rejected, (state, action) => {
+ state.status = 'failed';
+ state.error = action.error.message;
+ })
+ .addCase(userLogin.pending, (state) => {
+ state.status = 'loading';
+ })
+ .addCase(userLogin.fulfilled, (state, action) => {
+ state.authenticatedUser = action.payload.data;
+ state.status = action.payload.status === 200 ? 'succeeded' : 'failed';
+ })
+ .addCase(userLogin.rejected, (state, action) => {
+ state.status = 'failed';
+ state.error = action.error.message;
+ })
+ .addCase(userLogout.pending, (state) => {
+ state.status = 'loading';
+ })
+ .addCase(userLogout.fulfilled, (state, action) => {
+ state.authenticatedUser = {};
+ state.status = action.payload.status;
+ })
+ .addCase(userLogout.rejected, (state, action) => {
+ state.status = 'failed';
+ state.error = action.error.message;
+ })
+ .addCase(fetchUser.pending, (state) => {
+ state.status = 'loading';
+ })
+ .addCase(fetchUser.fulfilled, (state, action) => {
+ state.authenticatedUser = action.payload.data;
+ state.status = 'succeeded';
+ })
+ .addCase(fetchUser.rejected, (state, action) => {
+ state.status = 'failed';
+ state.error = action.error.message;
+ });
+ },
+});
+
+export default authenticationSlice.reducer;
diff --git a/src/redux/store.js b/src/redux/store.js
index 6d38af2..4e76c9c 100644
--- a/src/redux/store.js
+++ b/src/redux/store.js
@@ -1 +1,12 @@
-// local store file.
+import { configureStore } from '@reduxjs/toolkit';
+import logger from 'redux-logger';
+import authenticationReducer from './authentication/authenticationSlice';
+
+const store = configureStore({
+ reducer: {
+ authencation: authenticationReducer,
+ },
+ middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
+});
+
+export default store;
diff --git a/src/redux/thunk.js b/src/redux/thunk.js
index 8631bd9..28f9dea 100644
--- a/src/redux/thunk.js
+++ b/src/redux/thunk.js
@@ -1 +1,73 @@
-// api endpoint async methods.
+import { createAsyncThunk } from '@reduxjs/toolkit';
+import axios from 'axios';
+
+const baseURL = 'http://localhost:3000/api';
+
+const setAuthenticationToken = ({ headers }) => localStorage.setItem('token', headers.get('Authorization'));
+const removeAuthenticationToken = () => localStorage.removeItem('token');
+
+export const userSignUp = createAsyncThunk(
+ 'users/signup',
+ async (user, thunkAPI) => {
+ try {
+ const response = await axios.post(`${baseURL}/users/sign_up`, user, {
+ withCredentials: true,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(user),
+ });
+
+ setAuthenticationToken(response);
+
+ return response.data;
+ } catch (error) {
+ return thunkAPI.rejectWithValue(error);
+ }
+ },
+);
+
+export const userLogin = createAsyncThunk(
+ 'users/login',
+ async (user, thunkAPI) => {
+ try {
+ const response = await axios.post(`${baseURL}/users/sign_in`, user);
+
+ setAuthenticationToken(response);
+
+ return response.data;
+ } catch (error) {
+ return thunkAPI.rejectWithValue(error);
+ }
+ },
+);
+
+export const userLogout = createAsyncThunk(
+ 'users/logout',
+ async (_, thunkAPI) => {
+ try {
+ const response = await axios.delete(`${baseURL}/logout`);
+
+ removeAuthenticationToken();
+
+ return response.data;
+ } catch (error) {
+ return thunkAPI.rejectWithValue(error);
+ }
+ },
+);
+
+export const fetchUser = createAsyncThunk(
+ 'users/fetchUser',
+ async (_, thunkAPI) => {
+ try {
+ const response = await axios.get(`${baseURL}/users`);
+
+ setAuthenticationToken(response);
+
+ return response.data;
+ } catch (error) {
+ return thunkAPI.rejectWithValue(error);
+ }
+ },
+);