diff --git a/.gitignore b/.gitignore
index 4d29575..532eddc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,5 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+
+.env
\ No newline at end of file
diff --git a/src/App.js b/src/App.js
index e5a65f4..a76cb14 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,8 +1,15 @@
+import { useContext } from "react";
import Header from "components/Header";
import { Outlet } from "react-router-dom";
import { Container, Layout } from "./styles/globalStyles";
+import FirebaseAuthService from "./firebase/FirebaseAuthService";
+import GlobalContext from "store/context";
function App() {
+ const { setUser } = useContext(GlobalContext);
+
+ FirebaseAuthService.subscribeToAuthChanges(setUser);
+
return (
diff --git a/src/components/Login/SignIn/index.js b/src/components/Login/SignIn/index.js
new file mode 100644
index 0000000..438782e
--- /dev/null
+++ b/src/components/Login/SignIn/index.js
@@ -0,0 +1,97 @@
+import FirebaseAuthService from "../../../firebase/FirebaseAuthService";
+import { useState, useContext } from "react";
+import { Content, ButtonGroup, FormGroup } from "./styles";
+import MainButton from "components/MainButton";
+import GlobalContext from "store/context";
+
+const SignIn = () => {
+ const { user } = useContext(GlobalContext);
+ const [username, setUsername] = useState("");
+ const [password, setPassword] = useState("");
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ try {
+ await FirebaseAuthService.loginUser(username, password);
+ setUsername("");
+ setPassword("");
+ } catch (error) {
+ alert(error.message);
+ }
+ };
+
+ const handleLoginWithGoogle = async () => {
+ try {
+ await FirebaseAuthService.loginWithGoogle();
+ } catch (error) {
+ alert(error.message);
+ }
+ };
+
+ const handleSendResetPasswordEmail = async () => {
+ if (!username) {
+ alert("Missing username");
+ return;
+ }
+
+ try {
+ await FirebaseAuthService.resetPassword(username);
+ alert("Sent the password reset email");
+ } catch (error) {
+ alert(error.message);
+ }
+ };
+
+ const handleLogout = () => {
+ FirebaseAuthService.logoutUser();
+ };
+
+ return (
+
+ {user ? (
+
+
Welcome, {user.email}
+ Logout
+
+ ) : (
+ <>
+ Create an account
+
+
+
+
+
+
+
+
+
+
+ >
+ )}
+
+ );
+};
+
+export default SignIn;
diff --git a/src/components/Login/SignIn/styles.js b/src/components/Login/SignIn/styles.js
new file mode 100644
index 0000000..f12ddf7
--- /dev/null
+++ b/src/components/Login/SignIn/styles.js
@@ -0,0 +1,49 @@
+import styled from "styled-components";
+
+export const Content = styled.div`
+ height: 100%;
+ padding: 3rem 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-content: center;
+
+ & > h1 {
+ margin-bottom: 3rem;
+ }
+`;
+
+export const FormGroup = styled.form`
+ & > label {
+ font-size: 1.4rem;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: stretch;
+ & > input {
+ font-size: 1.6rem;
+ margin-bottom: 1rem;
+ padding: 1rem;
+ border-radius: 0.5rem;
+ border: none;
+ }
+ }
+`;
+
+export const ButtonGroup = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: stretch;
+ gap: 1rem;
+ & > button {
+ border: none;
+ padding: 1rem 5rem;
+ border-radius: 0.5rem;
+ background-color: #1aae9f;
+ color: #fff;
+ cursor: pointer;
+ font-weight: 600;
+ font-size: 1.6rem;
+ }
+`;
diff --git a/src/components/Login/SignUp/index.js b/src/components/Login/SignUp/index.js
new file mode 100644
index 0000000..c87682c
--- /dev/null
+++ b/src/components/Login/SignUp/index.js
@@ -0,0 +1,69 @@
+import FirebaseAuthService from "../../../firebase/FirebaseAuthService";
+import { useState, useContext } from "react";
+import { Content, ButtonGroup, FormGroup } from "./styles";
+import MainButton from "components/MainButton";
+import GlobalContext from "store/context";
+
+const SignUp = () => {
+ const { user } = useContext(GlobalContext);
+ const [username, setUsername] = useState("");
+ const [password, setPassword] = useState("");
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+
+ try {
+ await FirebaseAuthService.registerUser(username, password);
+ setUsername("");
+ setPassword("");
+ } catch (error) {
+ alert(error.message);
+ }
+ };
+
+ const handleLogout = () => {
+ FirebaseAuthService.logoutUser();
+ };
+
+ return (
+
+ {user ? (
+
+
Welcome, {user.email}
+ Logout
+
+ ) : (
+ <>
+ Create an account
+
+
+
+
+
+
+
+ >
+ )}
+
+ );
+};
+
+export default SignUp;
diff --git a/src/components/Login/SignUp/styles.js b/src/components/Login/SignUp/styles.js
new file mode 100644
index 0000000..2435c34
--- /dev/null
+++ b/src/components/Login/SignUp/styles.js
@@ -0,0 +1,48 @@
+import styled from "styled-components";
+
+export const Content = styled.div`
+ height: 100%;
+ padding: 3rem 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-content: center;
+
+ & > h1 {
+ margin-bottom: 3rem;
+ }
+`;
+
+export const FormGroup = styled.form`
+ & > label {
+ font-size: 1.4rem;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: stretch;
+ & > input {
+ font-size: 1.6rem;
+ margin-bottom: 1rem;
+ padding: 1rem;
+ border-radius: 0.5rem;
+ border: none;
+ }
+ }
+`;
+
+export const ButtonGroup = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: stretch;
+ & > button {
+ border: none;
+ padding: 1rem 5rem;
+ border-radius: 0.5rem;
+ background-color: #1aae9f;
+ color: #fff;
+ cursor: pointer;
+ font-weight: 600;
+ font-size: 1.6rem;
+ }
+`;
diff --git a/src/firebase/FirebaseAuthService.js b/src/firebase/FirebaseAuthService.js
new file mode 100644
index 0000000..1fc505c
--- /dev/null
+++ b/src/firebase/FirebaseAuthService.js
@@ -0,0 +1,65 @@
+import firebase from "./FirebaseConfig";
+import {
+ createUserWithEmailAndPassword,
+ signInWithEmailAndPassword,
+ sendPasswordResetEmail,
+ signInWithPopup,
+ GoogleAuthProvider,
+ onAuthStateChanged,
+} from "firebase/auth";
+
+const auth = firebase.auth;
+
+const registerUser = async (email, password) => {
+ try {
+ return await createUserWithEmailAndPassword(auth, email, password);
+ } catch (error) {
+ throw error;
+ }
+};
+
+const loginUser = async (email, password) => {
+ try {
+ return await signInWithEmailAndPassword(auth, email, password);
+ } catch (error) {
+ throw error;
+ }
+};
+
+const logoutUser = () => {
+ auth.signOut();
+};
+
+const loginWithGoogle = async () => {
+ const provider = new GoogleAuthProvider();
+ try {
+ return await signInWithPopup(auth, provider);
+ } catch (error) {
+ throw error;
+ }
+};
+
+const subscribeToAuthChanges = (handleAuthChanges) => {
+ onAuthStateChanged(auth, (user) => {
+ handleAuthChanges(user);
+ });
+};
+
+const resetPassword = async (email) => {
+ try {
+ return await sendPasswordResetEmail(auth, email);
+ } catch (error) {
+ throw error;
+ }
+};
+
+const FirebaseAuthService = {
+ registerUser,
+ loginUser,
+ logoutUser,
+ resetPassword,
+ loginWithGoogle,
+ subscribeToAuthChanges,
+};
+
+export default FirebaseAuthService;
diff --git a/src/firebase/FirebaseConfig.js b/src/firebase/FirebaseConfig.js
new file mode 100644
index 0000000..7c9b050
--- /dev/null
+++ b/src/firebase/FirebaseConfig.js
@@ -0,0 +1,25 @@
+import { initializeApp } from "firebase/app";
+import { getAnalytics } from "firebase/analytics";
+import { getAuth } from "firebase/auth";
+
+const config = {
+ apiKey: process.env.REACT_APP_API_KEY,
+ authDomain: process.env.REACT_APP_AUTH_DOMAIN,
+ projectId: process.env.REACT_APP_PROJECT_ID,
+ storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
+ messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID,
+ appId: process.env.REACT_APP_APP_ID,
+ measurementId: process.env.REACT_APP_MEASUREMENT_ID,
+};
+
+// Initialize Firebase
+const firebaseApp = initializeApp(config);
+const auth = getAuth(firebaseApp);
+const analytics = getAnalytics(firebaseApp);
+
+const firebaseConfig = {
+ auth,
+ analytics,
+};
+
+export default firebaseConfig;
diff --git a/src/index.js b/src/index.js
index f4bec6d..e7ab7c2 100644
--- a/src/index.js
+++ b/src/index.js
@@ -14,6 +14,8 @@ import EditTodo from "components/Todo/EditTodo";
import ViewTodo from "components/Todo/ViewTodo";
import Reorder from "components/Criteria/Reorder";
import EditCriteriaList from "components/Criteria/EditCriteriaList";
+import SignUp from "components/Login/SignUp";
+import SignIn from "components/Login/SignIn";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
@@ -24,6 +26,8 @@ root.render(
}>
+ }>
+ }>
}>
} />
} />
diff --git a/src/store/context/index.js b/src/store/context/index.js
index 329e2b0..d286287 100644
--- a/src/store/context/index.js
+++ b/src/store/context/index.js
@@ -7,6 +7,10 @@ const GlobalContext = createContext();
export const GlobalContextProvider = (props) => {
const [state, dispatch] = useReducer(reducer, mock);
+ const setUser = (user) => {
+ dispatch({ type: "SET_USER", payload: user });
+ };
+
const addTodo = (todo) => {
dispatch({ type: "ADD_TODO", payload: todo });
};
@@ -46,6 +50,7 @@ export const GlobalContextProvider = (props) => {
return (
{
switch (action.type) {
+ case "SET_USER":
+ return {
+ ...state,
+ user: action.payload,
+ };
case "ADD_CRITERIA":
return {
...state,