diff --git a/package-lock.json b/package-lock.json index a7a085fc..198a81ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@emotion/styled": "^11.11.5", "@mui/icons-material": "^5.16.6", "@mui/material": "^5.15.18", + "@react-oauth/google": "^0.12.1", "@reduxjs/toolkit": "^2.2.5", "@sentry/cli": "^2.32.1", "@sentry/react": "^7.114.0", @@ -19,6 +20,7 @@ "@tanstack/react-query": "^5.51.21", "axios": "^1.7.3", "chart.js": "^4.4.3", + "gapi-script": "^1.2.0", "js-cookie": "^3.0.5", "jwt-decode": "^4.0.0", "react": "^18.3.1", @@ -2577,6 +2579,7 @@ "version": "0.17.1", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz", "integrity": "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==", + "dev": true, "dependencies": { "@eslint/object-schema": "^2.1.4", "debug": "^4.3.1", @@ -2590,6 +2593,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -2612,6 +2616,7 @@ "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, "engines": { "node": ">=18" }, @@ -2623,6 +2628,7 @@ "version": "9.8.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.8.0.tgz", "integrity": "sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==", + "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -2631,6 +2637,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -2671,6 +2678,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, "engines": { "node": ">=18.18" }, @@ -4661,6 +4669,15 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@react-oauth/google": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.12.1.tgz", + "integrity": "sha512-qagsy22t+7UdkYAiT5ZhfM4StXi9PPNvw0zuwNmabrWyMKddczMtBIOARflbaIj+wHiQjnMAsZmzsUYuXeyoSg==", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/@reduxjs/toolkit": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.7.tgz", @@ -9325,6 +9342,7 @@ "version": "9.8.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.8.0.tgz", "integrity": "sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==", + "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", @@ -9483,6 +9501,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", + "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -9498,6 +9517,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -9509,6 +9529,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -9523,6 +9544,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -9538,6 +9560,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -9548,12 +9571,14 @@ "node_modules/eslint/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/eslint/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { "node": ">=8" } @@ -9562,6 +9587,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -9573,6 +9599,7 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", + "dev": true, "dependencies": { "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", @@ -9947,6 +9974,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, "dependencies": { "flat-cache": "^4.0.0" }, @@ -10106,6 +10134,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -10421,6 +10450,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gapi-script": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gapi-script/-/gapi-script-1.2.0.tgz", + "integrity": "sha512-NKTVKiIwFdkO1j1EzcrWu/Pz7gsl1GmBmgh+qhuV2Ytls04W/Eg5aiBL91SCiBM9lU0PMu7p1hTVxhh1rPT5Lw==" + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -11200,7 +11234,7 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", - "devOptional": true + "dev": true }, "node_modules/import-fresh": { "version": "3.3.0", @@ -20920,7 +20954,7 @@ "version": "1.77.8", "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", - "devOptional": true, + "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -22660,6 +22694,7 @@ "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index 635b52f7..3885c920 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@emotion/styled": "^11.11.5", "@mui/icons-material": "^5.16.6", "@mui/material": "^5.15.18", + "@react-oauth/google": "^0.12.1", "@reduxjs/toolkit": "^2.2.5", "@sentry/cli": "^2.32.1", "@sentry/react": "^7.114.0", @@ -29,6 +30,7 @@ "@tanstack/react-query": "^5.51.21", "axios": "^1.7.3", "chart.js": "^4.4.3", + "gapi-script": "^1.2.0", "js-cookie": "^3.0.5", "jwt-decode": "^4.0.0", "react": "^18.3.1", diff --git a/src/api/responses.ts b/src/api/responses.ts index 245ddbe7..015fe4b3 100644 --- a/src/api/responses.ts +++ b/src/api/responses.ts @@ -54,6 +54,7 @@ export type Login = { export type googleLogin = { message: string; + access_token: string; user: { email: string; first_name: string; diff --git a/src/features/auth/components/LoginForm.tsx b/src/features/auth/components/LoginForm.tsx index ba27cb16..83462e56 100644 --- a/src/features/auth/components/LoginForm.tsx +++ b/src/features/auth/components/LoginForm.tsx @@ -5,12 +5,9 @@ interface Props { isLoading: boolean; onLogin: (email: string, password: string) => void; onForgotPasswordClick: () => void; - handleGoogleAuth?: () => void; } -function LoginForm({ isLoading, onLogin, onForgotPasswordClick, handleGoogleAuth }: Props) { - const devMode = localStorage.getItem('devMode') === 'true'; - +function LoginForm({ isLoading, onLogin, onForgotPasswordClick }: Props) { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); @@ -36,7 +33,7 @@ function LoginForm({ isLoading, onLogin, onForgotPasswordClick, handleGoogleAuth
- {devMode && ( + {/* {devMode && ( } style={{ background: 'white', boxShadow: '0px 2px 3px 0px #0000002B, 0px 0px 3px 0px #00000015', border: 'none' }} /> - )} + )} */}
); diff --git a/src/features/auth/components/SignUpForm.tsx b/src/features/auth/components/SignUpForm.tsx index 23999de0..b54dd2db 100644 --- a/src/features/auth/components/SignUpForm.tsx +++ b/src/features/auth/components/SignUpForm.tsx @@ -1,6 +1,12 @@ 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 { useAppSelector } from 'store/hooks'; +import { useLogin } from '../hooks'; + +const GOOGLE_CLIENT_ID = process.env.REACT_APP_GOOGLE_CLIENT_ID; interface Props { isLoading: boolean; @@ -11,12 +17,16 @@ 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 { quizId } = useAppSelector((state) => state.auth.userA); + const { loginGoogleUser } = useLogin(); + function handleSubmit(e?: React.FormEvent) { e?.preventDefault(); if (!formIsValid) return; @@ -24,108 +34,111 @@ function SignUpForm({ isLoading, onSignUp }: Props) { onSignUp(firstname.value, lastname.value, email.value, password.value); } - function handleGoogleAuth() { - window.location.href = `${process.env.REACT_APP_API_URL}/register/google?quizId=${quizId}`; + 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 ( -
- 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} - /> - - 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} - /> - - 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="hello@climatemind.org" - type="email" - style={styles.textInput} - /> - - 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} - /> - - 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} - /> - -
- - {devMode && } -
- + +
+ 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} + /> + + 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} + /> + + 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="hello@climatemind.org" + type="email" + style={styles.textInput} + /> + + 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} + /> + + 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} + /> + +
+ + {devMode && handleGoogleError} text="continue_with" shape="pill" logo_alignment="left" theme="outline" />} +
+ +
); } diff --git a/src/features/auth/hooks/useLogin.tsx b/src/features/auth/hooks/useLogin.tsx index 6b9cae3e..094ab632 100644 --- a/src/features/auth/hooks/useLogin.tsx +++ b/src/features/auth/hooks/useLogin.tsx @@ -1,3 +1,5 @@ +import { CredentialResponse } from '@react-oauth/google'; + import { useAppDispatch } from 'store/hooks'; import { useApiClient, useToastMessage } from 'shared/hooks'; import { loginUserA as loginA, loginUserB as loginB } from '../state/authSlice'; @@ -16,13 +18,17 @@ function useLogin() { * Email and password are required. * @returns true if login was successful, false otherwise */ - - async function loginGoogleUser(emailCookie: string): Promise { + async function loginGoogleUser(response: CredentialResponse): Promise { try { - const data = await postGoogleLogin(emailCookie); + if (!response.credential) { + throw new Error('No credential received from Google'); + } + + const data = await postGoogleLogin(response.credential); showSuccessToast(`Welcome back, ${data.user.first_name}!`); const { first_name, last_name, email, quiz_id, user_uuid } = data.user; + dispatch( loginA({ firstName: first_name, diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index a64f1f43..a2b44c10 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -1,85 +1,84 @@ -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; -import Cookies from 'js-cookie'; +import { CredentialResponse, GoogleOAuthProvider, GoogleLogin } from '@react-oauth/google'; 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 { sendPasswordResetLink } = useResetPassword(); + const [showPasswordResetModal, setShowPasswordResetModal] = useState(false); async function handleSubmit(email: string, password: string) { setIsLoading(true); const isSuccessful = await loginA(email, password); if (isSuccessful) { - if (location.state && 'from' in location.state) { - navigate(location.state.from); - } else { - navigate(ROUTES.CLIMATE_FEED_PAGE); - } + navigateAfterLogin(); } setIsLoading(false); } - // Logic for password reset - const { sendPasswordResetLink } = useResetPassword(); - const [showPasswordResetModal, setShowPasswordResetModal] = useState(false); + function navigateAfterLogin() { + if (location.state && 'from' in location.state) { + navigate(location.state.from); + } else { + navigate(ROUTES.CLIMATE_FEED_PAGE); + } + } async function handlePasswordReset(email: string) { setShowPasswordResetModal(false); await sendPasswordResetLink(email); } - // useEffect for google authentication - useEffect(() => { - const urlParams = new URLSearchParams(window.location.search); - const accessToken = urlParams.get('access_token'); - - const emailCookie = Cookies.get('user_email'); - - async function fetchGoogleDetails() { - if (accessToken && emailCookie) { - setIsLoading(true); - const isSuccessful = await loginGoogleUser(emailCookie); - - if (isSuccessful) { - navigate(ROUTES.CLIMATE_FEED_PAGE); - } - setIsLoading(false); + const handleGoogleSuccess = async (credentialResponse: CredentialResponse) => { + // console.log('Google login success, credential:', credentialResponse); + setIsLoading(true); + try { + const isSuccessful = await loginGoogleUser(credentialResponse); + // console.log('loginGoogleUser result:', isSuccessful); + if (isSuccessful) { + navigateAfterLogin(); + } else if (!isSuccessful) { + navigate(ROUTES.PRE_QUIZ_PAGE); } + } catch (error) { + console.error('Error in loginGoogleUser:', error); } + setIsLoading(false); + }; - fetchGoogleDetails(); - }, [location.search, navigate]); - - const handleGoogleAuth = () => { - window.location.href = `${process.env.REACT_APP_API_URL}/login/google`; + const handleGoogleError = (error: any) => { + console.error('Google Login Failed:', error); }; return ( - - - {isMobile && navigate(-1)} style={styles.backButton} />} - - Climate Mind Logo - Climate Mind Logo - - setShowPasswordResetModal(true)} handleGoogleAuth={handleGoogleAuth} /> - - setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> - - + + + + {isMobile && navigate(-1)} style={styles.backButton} />} + + Climate Mind Logo + Climate Mind Logo + + setShowPasswordResetModal(true)} /> +
{devMode && handleGoogleError} shape="pill" logo_alignment="left" theme="outline" />}
+ setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> +
+
+
); } diff --git a/src/shared/hooks/useApiClient.tsx b/src/shared/hooks/useApiClient.tsx index c2727444..0d86ddbb 100644 --- a/src/shared/hooks/useApiClient.tsx +++ b/src/shared/hooks/useApiClient.tsx @@ -148,24 +148,23 @@ function useApiClient() { return response.data; } - async function postGoogleLogin(emailCookie: string) { - // pass through function as a param or set the access token here from the params? - const urlParams = new URLSearchParams(window.location.search); - const accessToken = urlParams.get('access_token'); - if (accessToken) Cookies.set('accessToken', accessToken, { secure: true }); + async function postGoogleLogin(credential: string) { + if (quizId) { + const response = await apiCall('post', '/auth/google', {}, { credential, quizId }, true); + return response.data; + } - const body = { - user_email: emailCookie, - }; - // create a type for googleLogin message and user{} - const response = await apiCall('post', '/login/google/getUserDetails', { Authorization: 'Bearer ' + accessToken, 'Content-Type': 'application/json' }, body, true); + const response = await apiCall('post', '/auth/google', {}, { credential }, true); + const { access_token } = response.data; + Cookies.set('accessToken', access_token, { secure: true, sameSite: 'strict' }); + console.log(response.data, 'data'); return response.data; } + async function postLogout() { // Remove the tokens from cookies Cookies.remove('accessToken'); Cookies.remove('refreshToken'); - await apiCall('post', '/logout', {}); }