Skip to content

Commit

Permalink
Merge pull request #21 from amitkeinan9/13-login-with-google
Browse files Browse the repository at this point in the history
13 login with google
  • Loading branch information
amitkeinan9 committed Jan 8, 2024
2 parents a968f4e + 5d675a4 commit b8d988d
Show file tree
Hide file tree
Showing 20 changed files with 726 additions and 47 deletions.
16 changes: 16 additions & 0 deletions Client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.15.3",
"@mui/material": "^5.15.2",
"@react-oauth/google": "^0.12.1",
"axios": "^1.6.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
5 changes: 4 additions & 1 deletion Client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import LoginPage from "./pages/login/LoginPage";
import { ThemeProvider } from "@emotion/react";
import { theme } from "./theme/theme";
import { HomePage } from "./pages/home/HomePage";
import { GoogleOAuthProvider } from "@react-oauth/google";
import { Layout } from "./components/Layout/Layout";
import { ProfilePage } from "./pages/profile/ProfilePage";

Expand Down Expand Up @@ -31,7 +32,9 @@ const router = createBrowserRouter([
function App() {
return (
<ThemeProvider theme={theme}>
<RouterProvider router={router} />
<GoogleOAuthProvider clientId="940068358470-1vud6iv0uj41vtkrho6msob9g6bhbm78.apps.googleusercontent.com">
<RouterProvider router={router} />
</GoogleOAuthProvider>
</ThemeProvider>
);
}
Expand Down
23 changes: 23 additions & 0 deletions Client/src/components/loadingButton/LoadingButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Button, ButtonProps, CircularProgress } from "@mui/material";

interface LoadingButtonProps extends ButtonProps {
isLoading: boolean;
loader?: JSX.Element;
}

export const LoadingButton = (props: LoadingButtonProps) => {
const { isLoading, loader, children, ...buttonProps } = props;

return (
<Button
{...buttonProps}
disabled={buttonProps.disabled || isLoading}
endIcon={!isLoading && buttonProps.endIcon}
startIcon={!isLoading && buttonProps.startIcon}
>
{isLoading
? loader ?? <CircularProgress size={22} color={buttonProps.color} />
: children}
</Button>
);
};
54 changes: 43 additions & 11 deletions Client/src/pages/login/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Alert, AlertTitle, Button, styled } from "@mui/material";
import {
Alert,
AlertTitle,
CircularProgress,
Divider,
styled,
} from "@mui/material";
import TextField from "@mui/material/TextField/TextField";
import React from "react";
import {
Expand All @@ -8,16 +14,24 @@ import {
logoStyles,
textButtonStyles,
actionButtonsStyles,
dividerStyles,
googleButtonStyles,
googleLoaderStyles,
} from "./styles";
import { useLoginForm } from "./useLoginForm";
import { useLoginFormActions } from "./useLoginFormActions";
import GoogleIcon from "@mui/icons-material/Google";
import { LoadingButton } from "../../components/loadingButton/LoadingButton";

const PageWrapper = styled("div")(pageWrapperStyles);
const Logo = styled("img")(logoStyles);
const LoginForm = styled("div")(loginFormStyles);
const InputFields = styled("div")(inputFieldsStyles);
const ActionButtons = styled("div")(actionButtonsStyles);
const TextButton = styled("p")(textButtonStyles);
const ActionsDivider = styled(Divider)(dividerStyles);
const GoogleButton = styled(LoadingButton)(googleButtonStyles);
const GoogleLoader = styled(CircularProgress)(googleLoaderStyles);

const LoginPage: React.FC = () => {
const {
Expand All @@ -33,11 +47,14 @@ const LoginPage: React.FC = () => {
setError,
} = useLoginForm();

const { handleLogin, handleRegister } = useLoginFormActions(
email,
password,
setError
);
const {
handleLogin,
isLoginLoading,
handleRegister,
isRegisterLoading,
handleGoogleLogin,
isGoogleLoading,
} = useLoginFormActions(email, password, setError);

return (
<PageWrapper>
Expand Down Expand Up @@ -66,32 +83,47 @@ const LoginPage: React.FC = () => {

<ActionButtons>
{inLoginMode ? (
<Button
<LoadingButton
isLoading={isLoginLoading}
fullWidth
variant="outlined"
color="primary"
onClick={handleLogin}
disabled={!isFormValid}
>
Login
</Button>
</LoadingButton>
) : (
<Button
<LoadingButton
isLoading={isRegisterLoading}
fullWidth
variant="outlined"
color="primary"
onClick={handleRegister}
disabled={!isFormValid}
>
Login
</Button>
Register
</LoadingButton>
)}

<TextButton onClick={toggleMode}>
{inLoginMode
? "Or create an account"
: "Already have an account? Login instead"}
</TextButton>

<ActionsDivider />

<GoogleButton
isLoading={isGoogleLoading}
loader={<GoogleLoader size={20} />}
onClick={handleGoogleLogin}
fullWidth
variant="outlined"
startIcon={<GoogleIcon />}
>
Continue with Google
</GoogleButton>
</ActionButtons>
</LoginForm>
</PageWrapper>
Expand Down
21 changes: 21 additions & 0 deletions Client/src/pages/login/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,24 @@ export const textButtonStyles = css({
color: purple[800],
},
});

export const dividerStyles = css({
margin: "20px 0",
});

const GOOGLE_COLOR = "#004C97";

export const googleButtonStyles = css({
color: GOOGLE_COLOR,
"&:hover": {
borderColor: GOOGLE_COLOR,
borderWidth: "2px",
backgroundColor: GOOGLE_COLOR,
color: "white",
},
});

export const googleLoaderStyles = css({
color: GOOGLE_COLOR,
padding: 2,
});
12 changes: 0 additions & 12 deletions Client/src/pages/login/useFinishLogin.ts

This file was deleted.

49 changes: 43 additions & 6 deletions Client/src/pages/login/useLoginFormActions.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,43 @@
import axios from "axios";
import { useFinishLogin } from "./useFinishLogin";
import { CodeResponse, useGoogleLogin } from "@react-oauth/google";
import { useState } from "react";
import { useNavigate } from "react-router-dom";

export const useLoginFormActions = (
email: string,
password: string,
setError: React.Dispatch<React.SetStateAction<string>>
) => {
const finishLogin = useFinishLogin();
const [isLoginLoading, setIsLoginLoading] = useState<boolean>(false);
const [isRegisterLoading, setIsRegisterLoading] = useState<boolean>(false);
const [isGoogleLoading, setIsGoogleLoading] = useState<boolean>(false);

const navigate = useNavigate();

const handleGoogleLogin = useGoogleLogin({
flow: "auth-code",
onSuccess: async (codeRes: CodeResponse) => {
setIsGoogleLoading(true);
await handleAction("/api/auth/google", codeRes);
setIsGoogleLoading(false);
},
});

const userData = { email, password };

const handleAction = async (apiRoute: string) => {
const finishLogin = (tokens: {
accessToken: string;
refreshToken: string;
}) => {
localStorage.setItem("accessToken", tokens.accessToken);
localStorage.setItem("refreshToken", tokens.refreshToken);

navigate("/home");
};

const handleAction = async (apiRoute: string, body?: object) => {
try {
const { data } = await axios.post(apiRoute, userData);
const { data } = await axios.post(apiRoute, body ?? userData);
finishLogin(data);
} catch (error) {
if (axios.isAxiosError(error)) {
Expand All @@ -25,12 +50,24 @@ export const useLoginFormActions = (
}
};

const handleLogin = () => handleAction("/api/auth/login");
const handleLogin = async () => {
setIsLoginLoading(true);
await handleAction("/api/auth/login");
setIsLoginLoading(false);
};

const handleRegister = () => handleAction("/api/auth/register");
const handleRegister = async () => {
setIsRegisterLoading(true);
handleAction("/api/auth/register");
setIsRegisterLoading(false);
};

return {
handleLogin,
isLoginLoading,
handleRegister,
isRegisterLoading,
handleGoogleLogin,
isGoogleLoading,
};
};
6 changes: 6 additions & 0 deletions Server/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
export default {
preset: "ts-jest",
testEnvironment: "node",
roots: ["<rootDir>/src"],
};
Loading

0 comments on commit b8d988d

Please sign in to comment.