Skip to content

Commit

Permalink
created custom google login component
Browse files Browse the repository at this point in the history
  • Loading branch information
epixieme committed Sep 2, 2024
1 parent 31c32e9 commit a6d0dbc
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 167 deletions.
58 changes: 58 additions & 0 deletions src/features/auth/components/GoogleLogin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { CredentialResponse } from '@react-oauth/google';
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import ROUTES from 'router/RouteConfig';
import { CmButton2 } from 'shared/components';
import { useLogin } from '../hooks';

function GoogleLogin({ navigateAfterLogin, text }: { navigateAfterLogin: () => void; text?: string }) {
const GOOGLE_CLIENT_ID = process.env.REACT_APP_GOOGLE_CLIENT_ID;
const [isLoading, setIsLoading] = useState(false);
const navigate = useNavigate();

const { loginGoogleUser } = useLogin();

const handleGoogleSuccess = async (credentialResponse: CredentialResponse) => {
setIsLoading(true);
try {
const isSuccessful = await loginGoogleUser(credentialResponse);
if (isSuccessful) {
navigateAfterLogin();
} else if (!isSuccessful) {
navigate(ROUTES.PRE_QUIZ_PAGE);
}
} catch (error) {
console.error('Error in loginGoogleUser:', error);
}
setIsLoading(false);
};

useEffect(() => {
/* Initialize Google API client */
(window as any).google.accounts.id.initialize({
client_id: GOOGLE_CLIENT_ID,
callback: handleCredentialResponse,
});
}, []);

const handleCredentialResponse = (response: any) => {
const credential = response.credential;
// Pass the credential to your login function
handleGoogleSuccess(credential);
};

const handleGoogleLogin = () => {
(window as any).google.accounts.id.prompt(); // Triggers the Google sign-in prompt
};
return (
<CmButton2
text={text}
isLoading={isLoading}
onClick={handleGoogleLogin}
startIcon={<img src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/google/google-original.svg" style={{ width: 24, height: 24 }} />}
style={{ background: 'white', boxShadow: '0px 2px 3px 0px #0000002B, 0px 0px 3px 0px #00000015', border: 'none' }}
/>
);
}

export default GoogleLogin;
15 changes: 1 addition & 14 deletions src/features/auth/components/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ interface Props {
isLoading: boolean;
onLogin: (email: string, password: string) => void;
onForgotPasswordClick: () => void;
onGoogleLogin: () => void;
}

function LoginForm({ isLoading, onLogin, onForgotPasswordClick, onGoogleLogin }: Props) {
const devMode = localStorage.getItem('devMode') === 'true';
function LoginForm({ isLoading, onLogin, onForgotPasswordClick }: Props) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');

Expand All @@ -34,17 +32,6 @@ function LoginForm({ isLoading, onLogin, onForgotPasswordClick, onGoogleLogin }:

<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-between', alignItems: 'center', marginTop: 30, gap: 20 }}>
<CmButton2 text="Log In" type="submit" isLoading={isLoading} disabled={!email || !password} onClick={handleSubmit} />
<div style={{ borderBottom: '1px solid #0000001A', height: 1, width: 205 }}></div>

{devMode && (
<CmButton2
text="Log In With Google"
isLoading={isLoading}
onClick={onGoogleLogin}
startIcon={<img src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/google/google-original.svg" style={{ width: 24, height: 24 }} />}
style={{ background: 'white', boxShadow: '0px 2px 3px 0px #0000002B, 0px 0px 3px 0px #00000015', border: 'none' }}
/>
)}
</div>
</form>
);
Expand Down
179 changes: 69 additions & 110 deletions src/features/auth/components/SignUpForm.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,21 @@
import { useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { CredentialResponse, GoogleLogin, GoogleOAuthProvider } from '@react-oauth/google';

import ROUTES from 'router/RouteConfig';
import { CmButton2, CmTextInput } from 'shared/components';
import { useLogin } from '../hooks';

const GOOGLE_CLIENT_ID = process.env.REACT_APP_GOOGLE_CLIENT_ID;
import GoogleLogin from './GoogleLogin';

interface Props {
isLoading: boolean;
onSignUp: (firstname: string, lastname: string, email: string, password: string) => void;
}

function SignUpForm({ isLoading, onSignUp }: Props) {
// For testing
const devMode = localStorage.getItem('devMode') === 'true';

const navigate = useNavigate();
const location = useLocation();

const [firstname, setFirstname] = useState({ value: '', touched: false });
const [lastname, setLastname] = useState({ value: '', touched: false });
const [email, setEmail] = useState({ value: '', touched: false });
const [password, setPassword] = useState({ value: '', touched: false });
const [confirmPassword, setConfirmPassword] = useState({ value: '', touched: false });
const { loginGoogleUser } = useLogin();

function handleSubmit(e?: React.FormEvent) {
e?.preventDefault();
Expand All @@ -34,111 +24,80 @@ function SignUpForm({ isLoading, onSignUp }: Props) {
onSignUp(firstname.value, lastname.value, email.value, password.value);
}

function navigateAfterLogin() {
if (location.state && 'from' in location.state) {
navigate(location.state.from);
} else {
navigate(ROUTES.CLIMATE_FEED_PAGE);
}
}

const handleGoogleSuccess = async (credentialResponse: CredentialResponse) => {
// setIsLoading(true);
try {
const isSuccessful = await loginGoogleUser(credentialResponse);

if (isSuccessful) {
navigateAfterLogin();
} else if (!isSuccessful) {
navigate(ROUTES.PRE_QUIZ_PAGE);
}
} catch (error) {
console.error('Error in loginGoogleUser:', error);
}
// setIsLoading(false);
};

const handleGoogleError = (error: any) => {
console.error('Google Login Failed:', error);
};

const emailValid = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email.value);
const passwordValid = /^(?=.*[a-zA-Z])(?=.*[\d!"#$£%&'()*+,-.:;<=>?@[\]^_`{|}~]).{8,128}$/.test(password.value);
const passwordsMatch = password.value === confirmPassword.value;
const formIsValid = emailValid && passwordValid && passwordsMatch && firstname.value !== '' && lastname.value !== '';

return (
<GoogleOAuthProvider clientId={GOOGLE_CLIENT_ID!}>
<form onSubmit={handleSubmit} style={styles.form}>
<CmTextInput
id="firstname"
label="First Name"
value={firstname.value}
onChange={(e) => setFirstname({ value: e.target.value, touched: firstname.touched })}
onBlur={() => setFirstname({ value: firstname.value, touched: true })}
error={firstname.touched && !firstname.value}
helperText={firstname.touched && !firstname.value && 'First name is required'}
placeholder="John"
style={styles.textInput}
/>

<CmTextInput
id="lastname"
label="Last Name"
value={lastname.value}
onChange={(e) => setLastname({ value: e.target.value, touched: lastname.touched })}
onBlur={() => setLastname({ value: lastname.value, touched: true })}
error={lastname.touched && !lastname.value}
helperText={lastname.touched && !lastname.value && 'Last name is required'}
placeholder="Smith"
style={styles.textInput}
/>

<CmTextInput
id="email"
label="Email"
value={email.value}
onChange={(e) => setEmail({ value: e.target.value, touched: email.touched })}
onBlur={() => setEmail({ value: email.value, touched: true })}
error={email.touched && !email.value}
helperText={email.touched && !emailValid && 'Invalid email address'}
placeholder="[email protected]"
type="email"
style={styles.textInput}
/>

<CmTextInput
id="password"
label="Password"
value={password.value}
onChange={(e) => setPassword({ value: e.target.value, touched: password.touched })}
onBlur={() => setPassword({ value: password.value, touched: true })}
error={password.touched && !passwordValid}
helperText={password.touched && !passwordValid && 'Invalid Password. Password must be at least 8 characters and contain one number or one special character'}
placeholder="Super Secret Password"
type="password"
style={styles.textInput}
/>

<CmTextInput
id="confirmPassword"
label="Confirm Password"
value={confirmPassword.value}
onChange={(e) => setConfirmPassword({ value: e.target.value, touched: confirmPassword.touched })}
onBlur={() => setConfirmPassword({ value: confirmPassword.value, touched: true })}
error={confirmPassword.touched && !passwordsMatch}
helperText={confirmPassword.touched && !passwordsMatch && 'Passwords do not match'}
placeholder="Confirm Password"
type="password"
style={styles.textInput}
/>

<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', marginTop: 40 }}>
<CmButton2 text="Create Account" isLoading={isLoading} disabled={!formIsValid} onClick={handleSubmit} />
{devMode && <GoogleLogin onSuccess={handleGoogleSuccess} onError={() => handleGoogleError} text="continue_with" shape="pill" logo_alignment="left" theme="outline" />}
</div>
</form>
</GoogleOAuthProvider>
<form onSubmit={handleSubmit} style={styles.form}>
<CmTextInput
id="firstname"
label="First Name"
value={firstname.value}
onChange={(e) => setFirstname({ value: e.target.value, touched: firstname.touched })}
onBlur={() => setFirstname({ value: firstname.value, touched: true })}
error={firstname.touched && !firstname.value}
helperText={firstname.touched && !firstname.value && 'First name is required'}
placeholder="John"
style={styles.textInput}
/>

<CmTextInput
id="lastname"
label="Last Name"
value={lastname.value}
onChange={(e) => setLastname({ value: e.target.value, touched: lastname.touched })}
onBlur={() => setLastname({ value: lastname.value, touched: true })}
error={lastname.touched && !lastname.value}
helperText={lastname.touched && !lastname.value && 'Last name is required'}
placeholder="Smith"
style={styles.textInput}
/>

<CmTextInput
id="email"
label="Email"
value={email.value}
onChange={(e) => setEmail({ value: e.target.value, touched: email.touched })}
onBlur={() => setEmail({ value: email.value, touched: true })}
error={email.touched && !email.value}
helperText={email.touched && !emailValid && 'Invalid email address'}
placeholder="[email protected]"
type="email"
style={styles.textInput}
/>

<CmTextInput
id="password"
label="Password"
value={password.value}
onChange={(e) => setPassword({ value: e.target.value, touched: password.touched })}
onBlur={() => setPassword({ value: password.value, touched: true })}
error={password.touched && !passwordValid}
helperText={password.touched && !passwordValid && 'Invalid Password. Password must be at least 8 characters and contain one number or one special character'}
placeholder="Super Secret Password"
type="password"
style={styles.textInput}
/>

<CmTextInput
id="confirmPassword"
label="Confirm Password"
value={confirmPassword.value}
onChange={(e) => setConfirmPassword({ value: e.target.value, touched: confirmPassword.touched })}
onBlur={() => setConfirmPassword({ value: confirmPassword.value, touched: true })}
error={confirmPassword.touched && !passwordsMatch}
helperText={confirmPassword.touched && !passwordsMatch && 'Passwords do not match'}
placeholder="Confirm Password"
type="password"
style={styles.textInput}
/>

<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', marginTop: 40 }}>
<CmButton2 text="Create Account" isLoading={isLoading} disabled={!formIsValid} onClick={handleSubmit} />
</div>
</form>
);
}

Expand Down
46 changes: 7 additions & 39 deletions src/pages/UserAUnauthorizedPages/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { CredentialResponse } from '@react-oauth/google';

import GoogleLogin from 'features/auth/components/GoogleLogin';
import ROUTES from 'router/RouteConfig';
import { CmBackButton, Page, PageContent } from 'shared/components';
import { LoginForm, RequestPasswordResetModal, useLogin, useResetPassword } from 'features/auth';
import { useMobileView } from 'shared/hooks';

const GOOGLE_CLIENT_ID = process.env.REACT_APP_GOOGLE_CLIENT_ID;

function LoginPage() {
const devMode = localStorage.getItem('devMode') === 'true';
const navigate = useNavigate();
const location = useLocation();
const isMobile = useMobileView();

const [isLoading, setIsLoading] = useState(false);
const { loginUserA: loginA, loginGoogleUser } = useLogin();
const { loginUserA: loginA } = useLogin();
const { sendPasswordResetLink } = useResetPassword();
const [showPasswordResetModal, setShowPasswordResetModal] = useState(false);

Expand All @@ -41,46 +40,15 @@ function LoginPage() {
await sendPasswordResetLink(email);
}

const handleGoogleSuccess = async (credentialResponse: CredentialResponse) => {
setIsLoading(true);
try {
const isSuccessful = await loginGoogleUser(credentialResponse);
if (isSuccessful) {
navigateAfterLogin();
} else if (!isSuccessful) {
navigate(ROUTES.PRE_QUIZ_PAGE);
}
} catch (error) {
console.error('Error in loginGoogleUser:', error);
}
setIsLoading(false);
};

useEffect(() => {
/* Initialize Google API client */
(window as any).google.accounts.id.initialize({
client_id: GOOGLE_CLIENT_ID,
callback: handleCredentialResponse,
});
}, []);

const handleCredentialResponse = (response: any) => {
const credential = response.credential;
// Pass the credential to your login function
handleGoogleSuccess(credential);
};

const handleGoogleLogin = () => {
(window as any).google.accounts.id.prompt(); // Triggers the Google sign-in prompt
};

return (
<Page style={{ background: 'white' }}>
<PageContent style={{ position: 'relative' }}>
{isMobile && <CmBackButton onClick={() => navigate(-1)} style={styles.backButton} />}
<img src="/logos/cm-logo.png" alt="Climate Mind Logo" style={styles.logo} />
<img src="/logos/slogan.png" alt="Climate Mind Logo" style={styles.slogan} />
<LoginForm isLoading={isLoading} onLogin={handleSubmit} onForgotPasswordClick={() => setShowPasswordResetModal(true)} onGoogleLogin={handleGoogleLogin} />
<LoginForm isLoading={isLoading} onLogin={handleSubmit} onForgotPasswordClick={() => setShowPasswordResetModal(true)} />
<div style={{ borderBottom: '1px solid #0000001A', height: 1, width: 205 }}></div>
{devMode && <GoogleLogin navigateAfterLogin={navigateAfterLogin} text="Log In With Google" />}
<RequestPasswordResetModal isOpen={showPasswordResetModal} onClose={() => setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} />
</PageContent>
</Page>
Expand Down
Loading

0 comments on commit a6d0dbc

Please sign in to comment.