From 8d6f9c8bcbdb94aae1052f2bd19a772e75b69140 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Sat, 3 Aug 2024 14:59:39 +0100 Subject: [PATCH 01/61] pushing changes to logic --- src/features/auth/hooks/useGoogleLogin.tsx | 34 +++++++++ .../UserAUnauthorizedPages/LoginPage.tsx | 74 +++++++++++-------- 2 files changed, 77 insertions(+), 31 deletions(-) create mode 100644 src/features/auth/hooks/useGoogleLogin.tsx diff --git a/src/features/auth/hooks/useGoogleLogin.tsx b/src/features/auth/hooks/useGoogleLogin.tsx new file mode 100644 index 00000000..4f321b37 --- /dev/null +++ b/src/features/auth/hooks/useGoogleLogin.tsx @@ -0,0 +1,34 @@ +// import { useEffect } from 'react'; +// function useGoogleLogin() { +// useEffect(() => { +// const urlParams = new URLSearchParams(window.location.search); +// const access_token = urlParams.get('access_token'); +// // const refresh_token = urlParams.get('refresh_token'); +// const first_name = Cookies.get('first_name'); +// const last_name = Cookies.get('last_name'); +// const email = Cookies.get('user_email'); +// const user_id = Cookies.get('user_uuid'); +// const quiz_id = Cookies.get('quiz_id'); + +// console.log(first_name, last_name, email, user_id, quiz_id); +// if (access_token) { +// //this sets the access token to be reused in the future +// Cookies.set('accessToken', access_token, { secure: true }); +// console.log(first_name, last_name, email, user_id, quiz_id); +// dispatch( +// loginUserA({ +// firstName: first_name as string, +// lastName: last_name as string, +// email: email as string, +// quizId: quiz_id as string, +// userId: user_id as string, +// }) +// ); +// navigate(ROUTES.CLIMATE_FEED_PAGE); +// } else { +// console.error('No access token found'); +// } +// }, [location.search, dispatch]); +// } + +// export default useGoogleLogin; diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index e4fd8ca9..912165d7 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -5,8 +5,8 @@ import ROUTES from 'router/RouteConfig'; import { CmTypography, Page, PageContent } from 'shared/components'; import { LoginForm, RequestPasswordResetModal, useLogin, useResetPassword, loginUserA } from 'features/auth'; -import Cookies from 'js-cookie'; import { useAppDispatch } from 'store/hooks'; +import Cookies from 'js-cookie'; function LoginPage() { const navigate = useNavigate(); @@ -42,41 +42,53 @@ function LoginPage() { } // useEffect for google authentification - + const userEmail = 'kirstie.l.hayes@googlemail.com'; useEffect(() => { const urlParams = new URLSearchParams(window.location.search); - const access_token = urlParams.get('access_token'); - // const refresh_token = urlParams.get('refresh_token'); - const first_name = Cookies.get('first_name'); - const last_name = Cookies.get('last_name'); - const email = Cookies.get('user_email'); - const user_id = Cookies.get('user_uuid'); - const quiz_id = Cookies.get('quiz_id'); - - console.log(first_name, last_name, email, user_id, quiz_id); - if (access_token) { - //this sets the access token to be reused in the future - Cookies.set('accessToken', access_token, { secure: true }); - console.log(first_name, last_name, email, user_id, quiz_id); - dispatch( - loginUserA({ - firstName: first_name as string, - lastName: last_name as string, - email: email as string, - quizId: quiz_id as string, - userId: user_id as string, - }) - ); - navigate(ROUTES.CLIMATE_FEED_PAGE); - } else { - console.error('No access token found'); + const accessToken = urlParams.get('access_token'); // Google returns an auth code + const user_email = Cookies.get('user_email'); + console.log(user_email, 'email'); + async function fetchUserDetails() { + if (accessToken) { + Cookies.set('accessToken', accessToken, { secure: true }); + const response = await fetch(`${process.env.REACT_APP_API_URL}/login/google/getUserDetails`, { + method: 'POST', + headers: { + Authorization: 'Bearer ' + accessToken, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + email: user_email, + }), + credentials: 'include', + }); + if (!response.ok) { + throw new Error('Network response was not ok'); + } + + const data = await response.json(); + + if (data.user) { + dispatch( + loginUserA({ + firstName: data.user.first_name as string, + lastName: data.user.last_name as string, + email: data.user.email as string, + quizId: data.user.quiz_id as string, + userId: data.user.user_id as string, + }) + ); + navigate(ROUTES.CLIMATE_FEED_PAGE); + } else { + throw new Error(data.error || 'User data not found'); + } + } } - }, [location.search, dispatch]); - const handleGoogleAuth = () => { - // Redirect to Google OAuth2 login endpoint - //need to set isloggedin to true so that the user is redirected to the climate feed page, set up a google auth redux userA slice + fetchUserDetails(); + }, [location.search, dispatch, navigate]); + const handleGoogleAuth = () => { window.location.href = `${process.env.REACT_APP_API_URL}/login/google`; }; From 7941304fd859e6a74f516f452f7e264d5bd2390c Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Sat, 3 Aug 2024 15:21:19 +0100 Subject: [PATCH 02/61] clean up --- src/pages/UserAUnauthorizedPages/LoginPage.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 912165d7..3654dd31 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -69,13 +69,14 @@ function LoginPage() { const data = await response.json(); if (data.user) { + const { first_name, last_name, email, quiz_id, user_id } = data.user; dispatch( loginUserA({ - firstName: data.user.first_name as string, - lastName: data.user.last_name as string, - email: data.user.email as string, - quizId: data.user.quiz_id as string, - userId: data.user.user_id as string, + firstName: first_name, + lastName: last_name, + email: email, + quizId: quiz_id, + userId: user_id, }) ); navigate(ROUTES.CLIMATE_FEED_PAGE); From 43ed5e437bc3ea303892b38979b791808b28caca Mon Sep 17 00:00:00 2001 From: Svenstar74 Date: Sun, 4 Aug 2024 08:48:05 +0200 Subject: [PATCH 03/61] update login and sign up screens --- public/logos/cm-logo.png | Bin 0 -> 8060 bytes public/logos/slogan.png | Bin 0 -> 17884 bytes src/features/auth/components/LoginForm.tsx | 25 ++++++----- src/features/auth/components/SignUpForm.tsx | 10 ++--- .../UserAUnauthorizedPages/LoginPage.tsx | 37 +++++++++++---- .../UserAUnauthorizedPages/SignUpPage.tsx | 6 +-- src/pages/UserBPages/UserBLoginPage.tsx | 37 ++++++++++++--- src/pages/UserBPages/UserBSignUpPage.tsx | 37 +++++++++++---- src/shared/components/CmBackButton.tsx | 6 +-- src/shared/components/CmButton2.tsx | 42 ++++++++++++++++++ src/shared/components/CmTypography.tsx | 12 ++++- src/shared/components/index.ts | 1 + src/shared/hooks/index.ts | 1 + src/shared/hooks/useMobileView.tsx | 21 +++++++++ 14 files changed, 187 insertions(+), 48 deletions(-) create mode 100644 public/logos/cm-logo.png create mode 100644 public/logos/slogan.png create mode 100644 src/shared/components/CmButton2.tsx create mode 100644 src/shared/hooks/useMobileView.tsx diff --git a/public/logos/cm-logo.png b/public/logos/cm-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..15eb44e6463f9d3fc18e16c7895959fd71d4f53b GIT binary patch literal 8060 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 KMNUMnLSTYWah0(E literal 0 HcmV?d00001 diff --git a/public/logos/slogan.png b/public/logos/slogan.png new file mode 100644 index 0000000000000000000000000000000000000000..a763c0be0be87bd8dc879a612bd16545158a789b GIT binary patch literal 17884 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 enBnrzKGx~JRon7&?}3N0FnGH9xvX void; onLogin: (email: string, password: string) => void; onForgotPasswordClick: () => void; } -function LoginForm({ isLoading, onCancel, onLogin, onForgotPasswordClick }: Props) { +function LoginForm({ isLoading, onLogin, onForgotPasswordClick }: Props) { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); @@ -43,13 +42,10 @@ function LoginForm({ isLoading, onCancel, onLogin, onForgotPasswordClick }: Prop
Forgot your password? - +
-
- {onCancel && } - -
+ ); } @@ -67,10 +63,17 @@ const styles: { [key: string]: React.CSSProperties } = { }, passwordResetContainer: { display: 'flex', + flexDirection: 'column', alignItems: 'center', - gap: 15, - marginTop: 10, - marginBottom: 30, + marginTop: 40, + marginBottom: '20%', + }, + resetLinkButton: { + textTransform: 'none', + textDecoration: 'underline', + letterSpacing: 0, + fontWeight: 800, + paddingTop: 0, }, }; diff --git a/src/features/auth/components/SignUpForm.tsx b/src/features/auth/components/SignUpForm.tsx index afee57f5..23999de0 100644 --- a/src/features/auth/components/SignUpForm.tsx +++ b/src/features/auth/components/SignUpForm.tsx @@ -1,14 +1,13 @@ import { useState } from 'react'; -import { CmButton, CmTextInput } from 'shared/components'; +import { CmButton2, CmTextInput } from 'shared/components'; import { useAppSelector } from 'store/hooks'; interface Props { isLoading: boolean; - onCancel?: () => void; onSignUp: (firstname: string, lastname: string, email: string, password: string) => void; } -function SignUpForm({ isLoading, onCancel, onSignUp }: Props) { +function SignUpForm({ isLoading, onSignUp }: Props) { // For testing const devMode = localStorage.getItem('devMode') === 'true'; @@ -99,9 +98,8 @@ function SignUpForm({ isLoading, onCancel, onSignUp }: Props) { style={styles.textInput} /> -
- {onCancel && } - +
+ {devMode && + ); +} + +const styles: { [key: string]: React.CSSProperties } = { + button: { + borderRadius: 100, + borderWidth: 1, + borderColor: '#07373B', + paddingTop: 10, + paddingBottom: 10, + paddingLeft: 36, + paddingRight: 29, + display: 'flex', + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'center', + alignSelf: 'center', + minWidth: 240, + }, +}; + +export default CmButton2; diff --git a/src/shared/components/CmTypography.tsx b/src/shared/components/CmTypography.tsx index bc620b36..6c8ca8e7 100644 --- a/src/shared/components/CmTypography.tsx +++ b/src/shared/components/CmTypography.tsx @@ -1,5 +1,5 @@ interface Props extends React.HTMLAttributes { - variant: 'h1' | 'h2' | 'h3' | 'h4' | 'body' | 'body-italics' | 'button' | 'caption' | 'label' | 'overline'; + variant: 'h1' | 'h2' | 'h3' | 'h4' | 'body' | 'body-italics' | 'button' | 'onboarding-button' | 'caption' | 'label' | 'overline'; children: React.ReactNode; style?: React.CSSProperties; } @@ -29,6 +29,9 @@ function CmTypography({ variant, children, style, ...rest }: Props) { case 'button': textStyle = styles.button; break; + case 'onboarding-button': + textStyle = styles.onboardingButton; + break; case 'caption': textStyle = styles.caption; break; @@ -110,6 +113,13 @@ const styles: { [key: string]: React.CSSProperties } = { textAlign: 'center', textTransform: 'uppercase', }, + onboardingButton: { + color: '#07373B', + fontFamily: "'Nunito', Arial, sans-serif", + fontSize: 16, + textAlign: 'center', + fontWeight: 'bold', + }, caption: { color: '#07373B', fontFamily: "'Nunito', Arial, sans-serif", diff --git a/src/shared/components/index.ts b/src/shared/components/index.ts index e2f541ac..5187c4a4 100644 --- a/src/shared/components/index.ts +++ b/src/shared/components/index.ts @@ -1,5 +1,6 @@ export { default as CmTypography } from './CmTypography'; export { default as CmButton } from './CmButton'; +export { default as CmButton2 } from './CmButton2'; export { default as CmBackButton } from './CmBackButton'; export { default as CmCarousel } from './CmCarousel'; export { default as CmTextInput } from './CmTextInput'; diff --git a/src/shared/hooks/index.ts b/src/shared/hooks/index.ts index 3afe09d8..658a6186 100644 --- a/src/shared/hooks/index.ts +++ b/src/shared/hooks/index.ts @@ -1,2 +1,3 @@ export { default as useApiClient } from './useApiClient'; export { default as useToastMessage } from './useToastMessage'; +export { default as useMobileView } from './useMobileView'; diff --git a/src/shared/hooks/useMobileView.tsx b/src/shared/hooks/useMobileView.tsx new file mode 100644 index 00000000..20060c2e --- /dev/null +++ b/src/shared/hooks/useMobileView.tsx @@ -0,0 +1,21 @@ +import { useState, useEffect } from 'react'; + +const useMobileView = () => { + const [isMobile, setIsMobile] = useState(window.innerWidth <= 768); + + useEffect(() => { + const handleResize = () => { + setIsMobile(window.innerWidth <= 768); + }; + + window.addEventListener('resize', handleResize); + + return () => { + window.removeEventListener('resize', handleResize); + }; + }, []); + + return isMobile; +}; + +export default useMobileView; From f372a2bf58c70679fd06271b0a69b9c2fb3f4fbd Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Sun, 4 Aug 2024 16:36:23 +0100 Subject: [PATCH 04/61] added fetch request --- src/pages/UserAUnauthorizedPages/LoginPage.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 3654dd31..221e0d1d 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -13,9 +13,8 @@ function LoginPage() { const location = useLocation(); const dispatch = useAppDispatch(); - // Logic for login const [isLoading, setIsLoading] = useState(false); - // const { loginUserA } = useLogin(); + const { loginUserA: loginA } = useLogin(); async function handleSubmit(email: string, password: string) { @@ -42,12 +41,12 @@ function LoginPage() { } // useEffect for google authentification - const userEmail = 'kirstie.l.hayes@googlemail.com'; + useEffect(() => { const urlParams = new URLSearchParams(window.location.search); const accessToken = urlParams.get('access_token'); // Google returns an auth code - const user_email = Cookies.get('user_email'); - console.log(user_email, 'email'); + const emailToken = urlParams.get('email_token'); + async function fetchUserDetails() { if (accessToken) { Cookies.set('accessToken', accessToken, { secure: true }); @@ -58,7 +57,7 @@ function LoginPage() { 'Content-Type': 'application/json', }, body: JSON.stringify({ - email: user_email, + email_token: emailToken, }), credentials: 'include', }); From 6785ca63b76d1974a693113994621f1b86eb6384 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Sun, 4 Aug 2024 21:25:00 +0100 Subject: [PATCH 05/61] refactored code to use the useApi hook --- src/api/responses.ts | 17 ++++++++-- .../UserAUnauthorizedPages/LoginPage.tsx | 33 +++++++------------ src/shared/components/CmButton.tsx | 17 +++++----- src/shared/hooks/useApiClient.tsx | 18 ++++++++-- 4 files changed, 50 insertions(+), 35 deletions(-) diff --git a/src/api/responses.ts b/src/api/responses.ts index ae998bcc..245ddbe7 100644 --- a/src/api/responses.ts +++ b/src/api/responses.ts @@ -1,6 +1,6 @@ -import { Alignment, ClimateEffect2, ClimateEffect3, Solution2, Solution3 } from "shared/types"; -import { TSharedImpact } from "types/SharedImpacts"; -import { TSharedSolution } from "types/SharedSolutions"; +import { Alignment, ClimateEffect2, ClimateEffect3, Solution2, Solution3 } from 'shared/types'; +import { TSharedImpact } from 'types/SharedImpacts'; +import { TSharedSolution } from 'types/SharedSolutions'; export type PostSession = { sessionId: string; @@ -52,6 +52,17 @@ export type Login = { }; }; +export type googleLogin = { + message: string; + user: { + email: string; + first_name: string; + last_name: string; + quiz_id: string; + user_uuid: string; + }; +}; + export type CreateConversation = { conversationId: string; message: string; diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 221e0d1d..6f5e14eb 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -2,16 +2,18 @@ import { useEffect, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import ROUTES from 'router/RouteConfig'; -import { CmTypography, Page, PageContent } from 'shared/components'; +import { CmButton, CmTypography, Page, PageContent } from 'shared/components'; import { LoginForm, RequestPasswordResetModal, useLogin, useResetPassword, loginUserA } from 'features/auth'; import { useAppDispatch } from 'store/hooks'; import Cookies from 'js-cookie'; +import { useApiClient } from 'shared/hooks'; function LoginPage() { const navigate = useNavigate(); const location = useLocation(); const dispatch = useAppDispatch(); + const { postGoogleLogin } = useApiClient(); const [isLoading, setIsLoading] = useState(false); @@ -45,42 +47,26 @@ function LoginPage() { useEffect(() => { const urlParams = new URLSearchParams(window.location.search); const accessToken = urlParams.get('access_token'); // Google returns an auth code - const emailToken = urlParams.get('email_token'); + const emailToken: string = urlParams.get('email_token') ?? ''; async function fetchUserDetails() { if (accessToken) { - Cookies.set('accessToken', accessToken, { secure: true }); - const response = await fetch(`${process.env.REACT_APP_API_URL}/login/google/getUserDetails`, { - method: 'POST', - headers: { - Authorization: 'Bearer ' + accessToken, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - email_token: emailToken, - }), - credentials: 'include', - }); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - - const data = await response.json(); + const data = await postGoogleLogin(emailToken); if (data.user) { - const { first_name, last_name, email, quiz_id, user_id } = data.user; + const { first_name, last_name, email, quiz_id, user_uuid } = data.user; dispatch( loginUserA({ firstName: first_name, lastName: last_name, email: email, quizId: quiz_id, - userId: user_id, + userId: user_uuid, }) ); navigate(ROUTES.CLIMATE_FEED_PAGE); } else { - throw new Error(data.error || 'User data not found'); + throw new Error('User data not found'); } } } @@ -103,6 +89,7 @@ function LoginPage() { Sign In setShowPasswordResetModal(true)} /> + ); } @@ -40,13 +40,14 @@ function CmButton({ text, onClick, color = 'success', variant = 'outlined', isLo const borderColor = color === 'error' ? 'red' : color === 'userb' ? '#a347ff' : '#39f5ad'; return ( -
); diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index b9e2eebe..384a6b78 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -70,32 +70,8 @@ function LoginPage() { Sign In - setShowPasswordResetModal(true)} /> - - + setShowPasswordResetModal(true)} handleGoogleAuth={handleGoogleAuth} /> + setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> diff --git a/src/pages/UserBPages/UserBLoginPage.tsx b/src/pages/UserBPages/UserBLoginPage.tsx index c42bc25c..35b8cf9d 100644 --- a/src/pages/UserBPages/UserBLoginPage.tsx +++ b/src/pages/UserBPages/UserBLoginPage.tsx @@ -27,14 +27,16 @@ function UserBLoginPage() { async function handlePasswordReset(email: string) { setShowPasswordResetModal(false); await sendPasswordResetLink(email); - }; + } return ( - Climate Mind Logo + Climate Mind Logo - Climate Mind + + Climate Mind + Sign In navigate(-1)} onForgotPasswordClick={() => setShowPasswordResetModal(true)} /> diff --git a/src/shared/components/CmButton.tsx b/src/shared/components/CmButton.tsx index c7a24a16..598bbfde 100644 --- a/src/shared/components/CmButton.tsx +++ b/src/shared/components/CmButton.tsx @@ -13,7 +13,7 @@ interface Props extends React.ButtonHTMLAttributes { children?: React.ReactNode; } -function CmButton({ text, onClick, color = 'success', variant = 'outlined', isLoading, style, startIcon, children, ...rest }: Props) { +function CmButton({ text, onClick, color = 'success', variant = 'outlined', isLoading, style, startIcon, ...rest }: Props) { if (variant === 'text') { return ( ); From d17230b8d7cdddc235a555375645537f341990c6 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:25:16 +0100 Subject: [PATCH 11/61] added devmode --- src/features/auth/components/LoginForm.tsx | 52 ++++++++++++---------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/features/auth/components/LoginForm.tsx b/src/features/auth/components/LoginForm.tsx index 7ebbf673..19fed8e0 100644 --- a/src/features/auth/components/LoginForm.tsx +++ b/src/features/auth/components/LoginForm.tsx @@ -10,6 +10,8 @@ interface Props { } function LoginForm({ isLoading, onCancel, onLogin, onForgotPasswordClick, handleGoogleAuth }: Props) { + const devMode = localStorage.getItem('devMode') === 'true'; + const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); @@ -34,30 +36,32 @@ function LoginForm({ isLoading, onCancel, onLogin, onForgotPasswordClick, handle
{onCancel && } - + {devMode && ( + + )}
); From 56e6f70e6787067cc962a387277f2ad0dd126c3f Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:40:43 +0100 Subject: [PATCH 12/61] clean up --- src/pages/UserAUnauthorizedPages/LoginPage.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 384a6b78..9400b958 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -79,5 +79,3 @@ function LoginPage() { } export default LoginPage; - -// set dev flag to hide google sign up and login From 7695289cafa0fecf5675e42bbd77610d8cc40114 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Tue, 6 Aug 2024 16:13:46 +0100 Subject: [PATCH 13/61] cleaned up --- src/features/auth/components/LoginForm.tsx | 9 +++------ src/pages/UserAUnauthorizedPages/LoginPage.tsx | 7 ------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/features/auth/components/LoginForm.tsx b/src/features/auth/components/LoginForm.tsx index 6563d1f0..75f759e7 100644 --- a/src/features/auth/components/LoginForm.tsx +++ b/src/features/auth/components/LoginForm.tsx @@ -8,10 +8,9 @@ interface Props { handleGoogleAuth?: () => void; } - -function LoginForm({ isLoading, onCancel, onLogin, onForgotPasswordClick, handleGoogleAuth }: Props) { +function LoginForm({ isLoading, onLogin, onForgotPasswordClick, handleGoogleAuth }: Props) { const devMode = localStorage.getItem('devMode') === 'true'; - + const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); @@ -35,8 +34,7 @@ function LoginForm({ isLoading, onCancel, onLogin, onForgotPasswordClick, handle
- {onCancel && } - + {devMode && (
- ); } diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index f2902bb5..26fa30d8 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -4,24 +4,18 @@ import { useLocation, useNavigate } from 'react-router-dom'; 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'; - function LoginPage() { // For testing - 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(); @@ -80,7 +74,6 @@ function LoginPage() { Climate Mind Logo Climate Mind Logo - setShowPasswordResetModal(true)} handleGoogleAuth={handleGoogleAuth} /> setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> From 1004c7cb5448247ddd584b3eccbb55b111218065 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Tue, 6 Aug 2024 16:32:26 +0100 Subject: [PATCH 14/61] pushing changes to button --- src/features/auth/components/LoginForm.tsx | 30 +++++----------------- src/shared/components/CmButton.tsx | 1 + src/shared/components/CmButton2.tsx | 13 +++++++--- 3 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/features/auth/components/LoginForm.tsx b/src/features/auth/components/LoginForm.tsx index 75f759e7..424e524d 100644 --- a/src/features/auth/components/LoginForm.tsx +++ b/src/features/auth/components/LoginForm.tsx @@ -35,31 +35,15 @@ function LoginForm({ isLoading, onLogin, onForgotPasswordClick, handleGoogleAuth
+ {devMode && ( - + startIcon={} + style={{ background: 'white', boxShadow: '0px 2px 3px 0px #0000002B, 0px 0px 3px 0px #00000015', border: 'none' }} + /> )}
diff --git a/src/shared/components/CmButton.tsx b/src/shared/components/CmButton.tsx index 598bbfde..c2a9a403 100644 --- a/src/shared/components/CmButton.tsx +++ b/src/shared/components/CmButton.tsx @@ -25,6 +25,7 @@ function CmButton({ text, onClick, color = 'success', variant = 'outlined', isLo color: rest.disabled || isLoading ? '#77AAAF' : 'black', cursor: rest.disabled ? 'default' : 'pointer', visibility: isLoading ? 'hidden' : 'visible', + ...style, }} onClick={rest.disabled ? () => {} : onClick} diff --git a/src/shared/components/CmButton2.tsx b/src/shared/components/CmButton2.tsx index 02fa8fb8..89c00b3c 100644 --- a/src/shared/components/CmButton2.tsx +++ b/src/shared/components/CmButton2.tsx @@ -1,22 +1,27 @@ import React from 'react'; import CircularProgress from '@mui/material/CircularProgress'; -import CmTypography from "./CmTypography"; +import CmTypography from './CmTypography'; interface Props extends React.ButtonHTMLAttributes { text?: string; variant?: 'light' | 'dark'; style?: React.CSSProperties; isLoading?: boolean; + startIcon?: React.ReactNode; } -function CmButton2({ text, variant = 'light', style, isLoading = false, ...rest }: Props) { +function CmButton2({ text, variant = 'light', style, isLoading = false, startIcon, ...rest }: Props) { const backgroundColor = variant === 'light' ? '#D0EEEB' : '#07373B'; const color = variant === 'light' ? '#07373B' : '#FFFFFF'; return ( ); } From 686e73be6516a5c984542ca57b6c16f8bdd8386a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 07:16:36 +0200 Subject: [PATCH 15/61] Bump @tanstack/react-query from 5.51.15 to 5.51.21 (#634) Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.51.15 to 5.51.21. - [Release notes](https://github.com/TanStack/query/releases) - [Commits](https://github.com/TanStack/query/commits/v5.51.21/packages/react-query) --- updated-dependencies: - dependency-name: "@tanstack/react-query" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 935907b7..230c4576 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@sentry/cli": "^2.32.1", "@sentry/react": "^7.114.0", "@sentry/tracing": "^7.114.0", - "@tanstack/react-query": "^5.49.2", + "@tanstack/react-query": "^5.51.21", "axios": "^1.7.2", "chart.js": "^4.4.3", "js-cookie": "^3.0.5", @@ -5245,20 +5245,20 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.51.15", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.51.15.tgz", - "integrity": "sha512-xyobHDJ0yhPE3+UkSQ2/4X1fLSg7ICJI5J1JyU9yf7F3deQfEwSImCDrB1WSRrauJkMtXW7YIEcC0oA6ZZWt5A==", + "version": "5.51.21", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.51.21.tgz", + "integrity": "sha512-POQxm42IUp6n89kKWF4IZi18v3fxQWFRolvBA6phNVmA8psdfB1MvDnGacCJdS+EOX12w/CyHM62z//rHmYmvw==", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" } }, "node_modules/@tanstack/react-query": { - "version": "5.51.15", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.51.15.tgz", - "integrity": "sha512-UgFg23SrdIYrmfTSxAUn9g+J64VQy11pb9/EefoY/u2+zWuNMeqEOnvpJhf52XQy0yztQoyM9p6x8PFyTNaxXg==", + "version": "5.51.21", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.51.21.tgz", + "integrity": "sha512-Q/V81x3sAYgCsxjwOkfLXfrmoG+FmDhLeHH5okC/Bp8Aaw2c33lbEo/mMcMnkxUPVtB2FLpzHT0tq3c+OlZEbw==", "dependencies": { - "@tanstack/query-core": "5.51.15" + "@tanstack/query-core": "5.51.21" }, "funding": { "type": "github", diff --git a/package.json b/package.json index 47cf739a..ad941b06 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "@sentry/cli": "^2.32.1", "@sentry/react": "^7.114.0", "@sentry/tracing": "^7.114.0", - "@tanstack/react-query": "^5.49.2", + "@tanstack/react-query": "^5.51.21", "axios": "^1.7.2", "chart.js": "^4.4.3", "js-cookie": "^3.0.5", From 6f2f5c09a05f1901fe30222f9067fdb437bc4370 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 07:19:21 +0200 Subject: [PATCH 16/61] Bump axios from 1.7.2 to 1.7.3 (#633) Bumps [axios](https://github.com/axios/axios) from 1.7.2 to 1.7.3. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.7.2...v1.7.3) --- updated-dependencies: - dependency-name: axios dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 230c4576..f71e3ef7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@sentry/react": "^7.114.0", "@sentry/tracing": "^7.114.0", "@tanstack/react-query": "^5.51.21", - "axios": "^1.7.2", + "axios": "^1.7.3", "chart.js": "^4.4.3", "js-cookie": "^3.0.5", "jwt-decode": "^4.0.0", @@ -6664,9 +6664,9 @@ } }, "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz", + "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", diff --git a/package.json b/package.json index ad941b06..5a89fd43 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@sentry/react": "^7.114.0", "@sentry/tracing": "^7.114.0", "@tanstack/react-query": "^5.51.21", - "axios": "^1.7.2", + "axios": "^1.7.3", "chart.js": "^4.4.3", "js-cookie": "^3.0.5", "jwt-decode": "^4.0.0", From ec346b1e7f8b3e219d654cccd7dc1f3a1620a64a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 07:21:12 +0200 Subject: [PATCH 17/61] Bump @mui/icons-material from 5.16.5 to 5.16.6 (#626) Bumps [@mui/icons-material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-icons-material) from 5.16.5 to 5.16.6. - [Release notes](https://github.com/mui/material-ui/releases) - [Changelog](https://github.com/mui/material-ui/blob/v5.16.6/CHANGELOG.md) - [Commits](https://github.com/mui/material-ui/commits/v5.16.6/packages/mui-icons-material) --- updated-dependencies: - dependency-name: "@mui/icons-material" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index f71e3ef7..7b45d8ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", - "@mui/icons-material": "^5.15.20", + "@mui/icons-material": "^5.16.6", "@mui/material": "^5.15.18", "@reduxjs/toolkit": "^2.2.5", "@sentry/cli": "^2.32.1", @@ -4322,9 +4322,9 @@ } }, "node_modules/@mui/icons-material": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.16.5.tgz", - "integrity": "sha512-bn88xxU/J9UV0s6+eutq7o3TTOrOlbCX+KshFb8kxgIxJZZfYz3JbAXVMivvoMF4Md6jCVUzM9HEkf4Ajab4tw==", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.16.6.tgz", + "integrity": "sha512-ceNGjoXheH9wbIFa1JHmSc9QVjJUvh18KvHrR4/FkJCSi9HXJ+9ee1kUhCOEFfuxNF8UB6WWVrIUOUgRd70t0A==", "dependencies": { "@babel/runtime": "^7.23.9" }, diff --git a/package.json b/package.json index 5a89fd43..d2df8ab5 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", - "@mui/icons-material": "^5.15.20", + "@mui/icons-material": "^5.16.6", "@mui/material": "^5.15.18", "@reduxjs/toolkit": "^2.2.5", "@sentry/cli": "^2.32.1", From f516f040a8c18de526332590e3d2c929a9522917 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 07:26:21 +0200 Subject: [PATCH 18/61] Bump uuid and @types/uuid (#608) Bumps [uuid](https://github.com/uuidjs/uuid) and [@types/uuid](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/uuid). These dependencies needed to be updated together. Updates `uuid` from 9.0.1 to 10.0.0 - [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md) - [Commits](https://github.com/uuidjs/uuid/compare/v9.0.1...v10.0.0) Updates `@types/uuid` from 9.0.8 to 10.0.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/uuid) --- updated-dependencies: - dependency-name: uuid dependency-type: direct:production update-type: version-update:semver-major - dependency-name: "@types/uuid" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 16 ++++++++-------- package.json | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7b45d8ab..a7a085fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,7 @@ "react-redux": "^9.1.2", "react-router-dom": "^6.24.1", "react-scripts": "^5.0.1", - "uuid": "^9.0.1" + "uuid": "^10.0.0" }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", @@ -40,7 +40,7 @@ "@types/react-dom": "^18.3.0", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", - "@types/uuid": "^9.0.8", + "@types/uuid": "^10.0.0", "cypress": "^13.13.2", "eslint": "^9.6.0", "sass": "^1.77.6", @@ -5731,9 +5731,9 @@ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" }, "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", "dev": true }, "node_modules/@types/ws": { @@ -22990,9 +22990,9 @@ } }, "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" diff --git a/package.json b/package.json index d2df8ab5..635b52f7 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "react-redux": "^9.1.2", "react-router-dom": "^6.24.1", "react-scripts": "^5.0.1", - "uuid": "^9.0.1" + "uuid": "^10.0.0" }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", @@ -50,7 +50,7 @@ "@types/react-dom": "^18.3.0", "@types/react-redux": "^7.1.33", "@types/react-router-dom": "^5.3.3", - "@types/uuid": "^9.0.8", + "@types/uuid": "^10.0.0", "cypress": "^13.13.2", "eslint": "^9.6.0", "sass": "^1.77.6", From d7ea4084a22bf82e7f587d98d3b2dfc6fb5d7f02 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Fri, 9 Aug 2024 10:26:01 +0100 Subject: [PATCH 19/61] 630 refactor google client logic (#636) * pushing changes to logic * clean up * added fetch request * refactored code to use the useApi hook * refactored google login to use useLogin hook * finished clean up for google login * made google login optional so it doesn't affect userb journey * added devmode * clean up * cleaned up * pushing changes to button * chore: Update Google login button text capitalization --------- Co-authored-by: Svenstar74 --- src/api/responses.ts | 17 +++- src/features/auth/components/LoginForm.tsx | 42 ++++----- src/features/auth/hooks/useLogin.tsx | 60 +++++++++---- .../UserAUnauthorizedPages/LoginPage.tsx | 88 +++++-------------- src/pages/UserBPages/UserBLoginPage.tsx | 3 +- src/shared/components/CmButton.tsx | 16 ++-- src/shared/components/CmButton2.tsx | 13 ++- src/shared/hooks/useApiClient.tsx | 18 +++- 8 files changed, 137 insertions(+), 120 deletions(-) diff --git a/src/api/responses.ts b/src/api/responses.ts index ae998bcc..245ddbe7 100644 --- a/src/api/responses.ts +++ b/src/api/responses.ts @@ -1,6 +1,6 @@ -import { Alignment, ClimateEffect2, ClimateEffect3, Solution2, Solution3 } from "shared/types"; -import { TSharedImpact } from "types/SharedImpacts"; -import { TSharedSolution } from "types/SharedSolutions"; +import { Alignment, ClimateEffect2, ClimateEffect3, Solution2, Solution3 } from 'shared/types'; +import { TSharedImpact } from 'types/SharedImpacts'; +import { TSharedSolution } from 'types/SharedSolutions'; export type PostSession = { sessionId: string; @@ -52,6 +52,17 @@ export type Login = { }; }; +export type googleLogin = { + message: string; + user: { + email: string; + first_name: string; + last_name: string; + quiz_id: string; + user_uuid: string; + }; +}; + export type CreateConversation = { conversationId: string; message: string; diff --git a/src/features/auth/components/LoginForm.tsx b/src/features/auth/components/LoginForm.tsx index bf174451..ba27cb16 100644 --- a/src/features/auth/components/LoginForm.tsx +++ b/src/features/auth/components/LoginForm.tsx @@ -5,9 +5,12 @@ interface Props { isLoading: boolean; onLogin: (email: string, password: string) => void; onForgotPasswordClick: () => void; + handleGoogleAuth?: () => void; } -function LoginForm({ isLoading, onLogin, onForgotPasswordClick }: Props) { +function LoginForm({ isLoading, onLogin, onForgotPasswordClick, handleGoogleAuth }: Props) { + const devMode = localStorage.getItem('devMode') === 'true'; + const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); @@ -20,32 +23,29 @@ function LoginForm({ isLoading, onLogin, onForgotPasswordClick }: Props) { return (
- setEmail(e.target.value)} - placeholder='hello@climatemind.org' - type='email' - style={styles.textInput} - /> + setEmail(e.target.value)} placeholder="hello@climatemind.org" type="email" style={styles.textInput} /> - setPassword(e.target.value)} - placeholder='Super Secret Password' - type='password' - style={styles.textInput} - /> + setPassword(e.target.value)} placeholder="Super Secret Password" type="password" style={styles.textInput} />
Forgot your password? - + +
- +
+ + + {devMode && ( + } + style={{ background: 'white', boxShadow: '0px 2px 3px 0px #0000002B, 0px 0px 3px 0px #00000015', border: 'none' }} + /> + )} +
); } diff --git a/src/features/auth/hooks/useLogin.tsx b/src/features/auth/hooks/useLogin.tsx index b6ff9b9b..b88db78e 100644 --- a/src/features/auth/hooks/useLogin.tsx +++ b/src/features/auth/hooks/useLogin.tsx @@ -7,26 +7,50 @@ function useLogin() { const apiClient = useApiClient(); const { showSuccessToast, showErrorToast } = useToastMessage(); + const { postGoogleLogin } = useApiClient(); /** * Login a userA, so that he can see his feeds, conversations, profile, etc. * On success we save the userA data in the store for later use. - * + * * Email and password are required. * @returns true if login was successful, false otherwise */ + + async function loginGoogleUser(emailToken: string): Promise { + try { + const data = await postGoogleLogin(emailToken); + showSuccessToast(`Welcome back, ${data.user.first_name}!`); + const { first_name, last_name, email, quiz_id, user_uuid } = data.user; + dispatch( + loginA({ + firstName: first_name, + lastName: last_name, + email: email, + userId: user_uuid, + quizId: quiz_id, + }) + ); + return true; + } catch (error) { + showErrorToast(error.response?.data.error ?? 'Unexpected Error. Please try again.'); + return false; + } + } async function loginUserA(email: string, password: string): Promise { try { const data = await apiClient.postLogin(email, password); showSuccessToast(`Welcome back, ${data.user.first_name}!`); - dispatch(loginA({ - firstName: data.user.first_name, - lastName: data.user.last_name, - email: data.user.email, - userId: data.user.user_uuid, - quizId: data.user.quiz_id, - })); + dispatch( + loginA({ + firstName: data.user.first_name, + lastName: data.user.last_name, + email: data.user.email, + userId: data.user.user_uuid, + quizId: data.user.quiz_id, + }) + ); return true; } catch (error) { showErrorToast(error.response?.data.error ?? 'Unexpected Error. Please try again.'); @@ -37,7 +61,7 @@ function useLogin() { /** * Login a userB, so that he can skip the quiz in the userB journey. * On success we save the userB data in the store for later use. - * + * * Email and password are required. * @returns true if login was successful, false otherwise */ @@ -45,13 +69,15 @@ function useLogin() { try { const data = await apiClient.postLogin(email, password, false); - dispatch(loginB({ - firstName: data.user.first_name, - lastName: data.user.last_name, - email: data.user.email, - userId: data.user.user_uuid, - quizId: data.user.quiz_id, - })); + dispatch( + loginB({ + firstName: data.user.first_name, + lastName: data.user.last_name, + email: data.user.email, + userId: data.user.user_uuid, + quizId: data.user.quiz_id, + }) + ); return true; } catch (error) { @@ -60,7 +86,7 @@ function useLogin() { } } - return { loginUserA, loginUserB }; + return { loginUserA, loginUserB, loginGoogleUser }; } export default useLogin; diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 183c3ba9..26fa30d8 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -4,24 +4,21 @@ import { useLocation, useNavigate } from 'react-router-dom'; import ROUTES from 'router/RouteConfig'; import { CmBackButton, Page, PageContent } from 'shared/components'; -import { LoginForm, RequestPasswordResetModal, useLogin, useResetPassword, loginUserA } from 'features/auth'; -import Cookies from 'js-cookie'; -import { useAppDispatch } from 'store/hooks'; +import { LoginForm, RequestPasswordResetModal, useLogin, useResetPassword } from 'features/auth'; + import { useMobileView } from 'shared/hooks'; function LoginPage() { // For testing - const devMode = localStorage.getItem('devMode') === 'true'; const navigate = useNavigate(); const location = useLocation(); - const dispatch = useAppDispatch(); + const isMobile = useMobileView(); - // Logic for login const [isLoading, setIsLoading] = useState(false); - // const { loginUserA } = useLogin(); - const { loginUserA: loginA } = useLogin(); + + const { loginUserA: loginA, loginGoogleUser } = useLogin(); async function handleSubmit(email: string, password: string) { setIsLoading(true); @@ -33,7 +30,6 @@ function LoginPage() { navigate(ROUTES.CLIMATE_FEED_PAGE); } } - setIsLoading(false); } @@ -46,42 +42,27 @@ function LoginPage() { await sendPasswordResetLink(email); } - // useEffect for google authentification - + // useEffect for google authentication useEffect(() => { const urlParams = new URLSearchParams(window.location.search); - const access_token = urlParams.get('access_token'); - // const refresh_token = urlParams.get('refresh_token'); - const first_name = Cookies.get('first_name'); - const last_name = Cookies.get('last_name'); - const email = Cookies.get('user_email'); - const user_id = Cookies.get('user_uuid'); - const quiz_id = Cookies.get('quiz_id'); - - console.log(first_name, last_name, email, user_id, quiz_id); - if (access_token) { - //this sets the access token to be reused in the future - Cookies.set('accessToken', access_token, { secure: true }); - console.log(first_name, last_name, email, user_id, quiz_id); - dispatch( - loginUserA({ - firstName: first_name as string, - lastName: last_name as string, - email: email as string, - quizId: quiz_id as string, - userId: user_id as string, - }) - ); - navigate(ROUTES.CLIMATE_FEED_PAGE); - } else { - console.error('No access token found'); + const accessToken = urlParams.get('access_token'); + const emailToken: string = urlParams.get('email_token') ?? ''; + + async function fetchGoogleDetails() { + if (accessToken) { + setIsLoading(true); + const isSuccessful = await loginGoogleUser(emailToken); + if (isSuccessful) { + navigate(ROUTES.CLIMATE_FEED_PAGE); + } + setIsLoading(false); + } } - }, [location.search, dispatch]); - const handleGoogleAuth = () => { - // Redirect to Google OAuth2 login endpoint - //need to set isloggedin to true so that the user is redirected to the climate feed page, set up a google auth redux userA slice + fetchGoogleDetails(); + }, [location.search, navigate]); + const handleGoogleAuth = () => { window.location.href = `${process.env.REACT_APP_API_URL}/login/google`; }; @@ -93,31 +74,8 @@ function LoginPage() { Climate Mind Logo Climate Mind Logo - setShowPasswordResetModal(true)} /> - {devMode && } + setShowPasswordResetModal(true)} handleGoogleAuth={handleGoogleAuth} /> + setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> diff --git a/src/pages/UserBPages/UserBLoginPage.tsx b/src/pages/UserBPages/UserBLoginPage.tsx index d1d77cf3..e63d58c2 100644 --- a/src/pages/UserBPages/UserBLoginPage.tsx +++ b/src/pages/UserBPages/UserBLoginPage.tsx @@ -29,9 +29,10 @@ function UserBLoginPage() { async function handlePasswordReset(email: string) { setShowPasswordResetModal(false); await sendPasswordResetLink(email); - }; + } return ( + {isMobile && navigate(-1)} style={styles.backButton} />} diff --git a/src/shared/components/CmButton.tsx b/src/shared/components/CmButton.tsx index 60aee487..c2a9a403 100644 --- a/src/shared/components/CmButton.tsx +++ b/src/shared/components/CmButton.tsx @@ -10,6 +10,7 @@ interface Props extends React.ButtonHTMLAttributes { style?: React.CSSProperties; startIcon?: React.ReactNode; isLoading?: boolean; + children?: React.ReactNode; } function CmButton({ text, onClick, color = 'success', variant = 'outlined', isLoading, style, startIcon, ...rest }: Props) { @@ -21,18 +22,18 @@ function CmButton({ text, onClick, color = 'success', variant = 'outlined', isLo style={{ padding: 5, marginLeft: startIcon ? 10 : 0, - color: (rest.disabled || isLoading) ? '#77AAAF' : 'black', + color: rest.disabled || isLoading ? '#77AAAF' : 'black', cursor: rest.disabled ? 'default' : 'pointer', visibility: isLoading ? 'hidden' : 'visible', + ...style, }} onClick={rest.disabled ? () => {} : onClick} > {text} -
- {isLoading && } -
+ +
{isLoading && }
); } @@ -40,13 +41,14 @@ function CmButton({ text, onClick, color = 'success', variant = 'outlined', isLo const borderColor = color === 'error' ? 'red' : color === 'userb' ? '#a347ff' : '#39f5ad'; return ( - ); } diff --git a/src/shared/hooks/useApiClient.tsx b/src/shared/hooks/useApiClient.tsx index 8da005b7..fd79f798 100644 --- a/src/shared/hooks/useApiClient.tsx +++ b/src/shared/hooks/useApiClient.tsx @@ -26,12 +26,11 @@ const validateToken = (token: string): boolean => { function useApiClient() { const { showErrorToast } = useToastMessage(); - // const { logoutUserA } = useLogout(); const sessionId = useAppSelector((state) => state.auth.userA.sessionId); const quizId = useAppSelector((state) => state.auth.userA.quizId); - async function apiCall(method: string, endpoint: string, headers: { [key: string]: string }, data?: any) { + async function apiCall(method: string, endpoint: string, headers: { [key: string]: string }, data?: any, withCredentials?: boolean) { // Add sessionId to headers if (sessionId) { headers['X-Session-Id'] = sessionId; @@ -54,6 +53,7 @@ function useApiClient() { method, headers, data, + withCredentials, }); return response; @@ -148,6 +148,19 @@ function useApiClient() { return response.data; } + async function postGoogleLogin(emailToken: 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 }); + + const body = { + email_token: emailToken, + }; + // 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); + return response.data; + } async function postLogout() { // Remove the tokens from cookies Cookies.remove('accessToken'); @@ -447,6 +460,7 @@ function useApiClient() { postRegister, deleteAccount, postLogin, + postGoogleLogin, postLogout, postRefresh, checkPasswordResetLink, From e7742660ee20bed2ab99e5003146e113b4e186dd Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:57:59 +0100 Subject: [PATCH 20/61] pushing changes --- src/features/auth/hooks/useLogin.tsx | 4 ++-- src/pages/UserAUnauthorizedPages/LoginPage.tsx | 8 +++++--- src/shared/hooks/useApiClient.tsx | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/features/auth/hooks/useLogin.tsx b/src/features/auth/hooks/useLogin.tsx index b88db78e..21f34dcc 100644 --- a/src/features/auth/hooks/useLogin.tsx +++ b/src/features/auth/hooks/useLogin.tsx @@ -17,9 +17,9 @@ function useLogin() { * @returns true if login was successful, false otherwise */ - async function loginGoogleUser(emailToken: string): Promise { + async function loginGoogleUser(emailCookie: string): Promise { try { - const data = await postGoogleLogin(emailToken); + const data = await postGoogleLogin(emailCookie); showSuccessToast(`Welcome back, ${data.user.first_name}!`); const { first_name, last_name, email, quiz_id, user_uuid } = data.user; dispatch( diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 26fa30d8..67c44018 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -7,6 +7,7 @@ import { CmBackButton, Page, PageContent } from 'shared/components'; import { LoginForm, RequestPasswordResetModal, useLogin, useResetPassword } from 'features/auth'; import { useMobileView } from 'shared/hooks'; +import Cookies from 'js-cookie'; function LoginPage() { // For testing @@ -46,12 +47,13 @@ function LoginPage() { useEffect(() => { const urlParams = new URLSearchParams(window.location.search); const accessToken = urlParams.get('access_token'); - const emailToken: string = urlParams.get('email_token') ?? ''; + const emailCookie = Cookies.get('user_email'); + console.log(emailCookie); async function fetchGoogleDetails() { - if (accessToken) { + if (accessToken && emailCookie) { setIsLoading(true); - const isSuccessful = await loginGoogleUser(emailToken); + const isSuccessful = await loginGoogleUser(emailCookie); if (isSuccessful) { navigate(ROUTES.CLIMATE_FEED_PAGE); } diff --git a/src/shared/hooks/useApiClient.tsx b/src/shared/hooks/useApiClient.tsx index fd79f798..c2727444 100644 --- a/src/shared/hooks/useApiClient.tsx +++ b/src/shared/hooks/useApiClient.tsx @@ -148,14 +148,14 @@ function useApiClient() { return response.data; } - async function postGoogleLogin(emailToken: string) { + 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 }); const body = { - email_token: emailToken, + 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); From 3965a7fb5889a04d1c2695e8fd31c234247ab6c2 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Tue, 13 Aug 2024 12:15:41 +0100 Subject: [PATCH 21/61] clean up --- src/pages/UserAUnauthorizedPages/LoginPage.tsx | 3 --- src/shared/hooks/useApiClient.tsx | 4 ---- 2 files changed, 7 deletions(-) diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 4d74f57e..338f2d59 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -10,8 +10,6 @@ import { useMobileView } from 'shared/hooks'; import Cookies from 'js-cookie'; function LoginPage() { - // For testing - const navigate = useNavigate(); const location = useLocation(); @@ -50,7 +48,6 @@ function LoginPage() { const emailCookie = Cookies.get('user_email'); - async function fetchGoogleDetails() { if (accessToken && emailCookie) { setIsLoading(true); diff --git a/src/shared/hooks/useApiClient.tsx b/src/shared/hooks/useApiClient.tsx index 3ff51ea8..c2727444 100644 --- a/src/shared/hooks/useApiClient.tsx +++ b/src/shared/hooks/useApiClient.tsx @@ -148,18 +148,14 @@ 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 }); 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); From 957cba44214908a9098d7655c23835e87a7d38e0 Mon Sep 17 00:00:00 2001 From: Svenstar74 Date: Fri, 16 Aug 2024 15:16:16 +0200 Subject: [PATCH 22/61] cleanup --- src/features/auth/hooks/useLogin.tsx | 1 - src/pages/UserAUnauthorizedPages/LoginPage.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/features/auth/hooks/useLogin.tsx b/src/features/auth/hooks/useLogin.tsx index ff0d498a..6b9cae3e 100644 --- a/src/features/auth/hooks/useLogin.tsx +++ b/src/features/auth/hooks/useLogin.tsx @@ -17,7 +17,6 @@ function useLogin() { * @returns true if login was successful, false otherwise */ - async function loginGoogleUser(emailCookie: string): Promise { try { const data = await postGoogleLogin(emailCookie); diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 338f2d59..a64f1f43 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; +import Cookies from 'js-cookie'; import ROUTES from 'router/RouteConfig'; import { CmBackButton, Page, PageContent } from 'shared/components'; @@ -7,7 +8,6 @@ import { CmBackButton, Page, PageContent } from 'shared/components'; import { LoginForm, RequestPasswordResetModal, useLogin, useResetPassword } from 'features/auth'; import { useMobileView } from 'shared/hooks'; -import Cookies from 'js-cookie'; function LoginPage() { const navigate = useNavigate(); From 913bc4f91209b5b7c1c322948ec370a150d3894c Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Thu, 22 Aug 2024 20:03:23 +0100 Subject: [PATCH 23/61] google login code in place --- package-lock.json | 56 +++++++++++- package.json | 3 + src/api/responses.ts | 1 + src/features/auth/hooks/useLogin.tsx | 10 ++- .../UserAUnauthorizedPages/LoginPage.tsx | 87 ++++++++++--------- src/shared/hooks/useApiClient.tsx | 16 ++-- 6 files changed, 113 insertions(+), 60 deletions(-) diff --git a/package-lock.json b/package-lock.json index a7a085fc..b562bb1f 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,12 +20,14 @@ "@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", "react-chartjs-2": "^5.2.0", "react-device-detect": "^2.2.3", "react-dom": "^18.3.1", + "react-google-login": "^5.2.2", "react-markdown": "^9.0.1", "react-query": "^3.39.3", "react-redux": "^9.1.2", @@ -2577,6 +2580,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 +2594,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 +2617,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 +2629,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 +2638,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 +2679,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 +4670,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 +9343,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 +9502,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 +9518,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 +9530,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 +9545,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 +9561,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 +9572,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 +9588,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 +9600,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 +9975,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 +10135,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 +10451,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 +11235,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", @@ -19524,6 +19559,20 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "node_modules/react-google-login": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/react-google-login/-/react-google-login-5.2.2.tgz", + "integrity": "sha512-JUngfvaSMcOuV0lFff7+SzJ2qviuNMQdqlsDJkUM145xkGPVIfqWXq9Ui+2Dr6jdJWH5KYdynz9+4CzKjI5u6g==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dependencies": { + "@types/react": "*", + "prop-types": "^15.6.0" + }, + "peerDependencies": { + "react": "^16 || ^17", + "react-dom": "^16 || ^17" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -20920,7 +20969,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 +22709,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..a42ced46 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,12 +30,14 @@ "@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", "react-chartjs-2": "^5.2.0", "react-device-detect": "^2.2.3", "react-dom": "^18.3.1", + "react-google-login": "^5.2.2", "react-markdown": "^9.0.1", "react-query": "^3.39.3", "react-redux": "^9.1.2", 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/hooks/useLogin.tsx b/src/features/auth/hooks/useLogin.tsx index 6b9cae3e..1cd7a721 100644 --- a/src/features/auth/hooks/useLogin.tsx +++ b/src/features/auth/hooks/useLogin.tsx @@ -1,7 +1,7 @@ import { useAppDispatch } from 'store/hooks'; import { useApiClient, useToastMessage } from 'shared/hooks'; import { loginUserA as loginA, loginUserB as loginB } from '../state/authSlice'; - +import { CredentialResponse } from '@react-oauth/google'; function useLogin() { const dispatch = useAppDispatch(); @@ -17,9 +17,13 @@ function useLogin() { * @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); + console.log(data); showSuccessToast(`Welcome back, ${data.user.first_name}!`); const { first_name, last_name, email, quiz_id, user_uuid } = data.user; diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index a64f1f43..4a35e623 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -1,85 +1,86 @@ import { useEffect, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import Cookies from 'js-cookie'; +import { GoogleOAuthProvider, GoogleLogin } from '@react-oauth/google'; +import { CredentialResponse } 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; +console.log(GOOGLE_CLIENT_ID); + function LoginPage() { 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); - 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); + function navigateAfterLogin() { + if (location.state && 'from' in location.state) { + navigate(location.state.from); + } else { + navigate(ROUTES.CLIMATE_FEED_PAGE); + } + } - 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} />} + + + + {isMobile && navigate(-1)} style={styles.backButton} />} + + Climate Mind Logo + Climate Mind Logo - Climate Mind Logo - Climate Mind Logo + setShowPasswordResetModal(true)} /> - setShowPasswordResetModal(true)} handleGoogleAuth={handleGoogleAuth} /> + - setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> - - + setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> + + + ); } diff --git a/src/shared/hooks/useApiClient.tsx b/src/shared/hooks/useApiClient.tsx index c2727444..8c8fd3cf 100644 --- a/src/shared/hooks/useApiClient.tsx +++ b/src/shared/hooks/useApiClient.tsx @@ -148,19 +148,13 @@ 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 }); - - 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); + async function postGoogleLogin(credential: string) { + const response = await apiCall('post', '/auth/google', {}, { credential }, true); + const { access_token, user } = response.data; + Cookies.set('accessToken', access_token, { secure: true, sameSite: 'strict' }); return response.data; } + async function postLogout() { // Remove the tokens from cookies Cookies.remove('accessToken'); From e8bb583c4751c8536ec0668e618103332389147d Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Fri, 23 Aug 2024 12:14:25 +0100 Subject: [PATCH 24/61] pushing working code ready for refactoring --- src/features/auth/components/SignUpForm.tsx | 213 +++++++++++++------- src/features/auth/hooks/useLogin.tsx | 2 +- src/shared/hooks/useApiClient.tsx | 6 +- 3 files changed, 143 insertions(+), 78 deletions(-) diff --git a/src/features/auth/components/SignUpForm.tsx b/src/features/auth/components/SignUpForm.tsx index 23999de0..302d6a75 100644 --- a/src/features/auth/components/SignUpForm.tsx +++ b/src/features/auth/components/SignUpForm.tsx @@ -1,12 +1,17 @@ +import { CredentialResponse, GoogleLogin, GoogleOAuthProvider } from '@react-oauth/google'; import { useState } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import ROUTES from 'router/RouteConfig'; import { CmButton2, CmTextInput } from 'shared/components'; import { useAppSelector } from 'store/hooks'; +import { useLogin } from '../hooks'; interface Props { isLoading: boolean; onSignUp: (firstname: string, lastname: string, email: string, password: string) => void; } +const GOOGLE_CLIENT_ID = process.env.REACT_APP_GOOGLE_CLIENT_ID; function SignUpForm({ isLoading, onSignUp }: Props) { // For testing const devMode = localStorage.getItem('devMode') === 'true'; @@ -17,6 +22,9 @@ function SignUpForm({ isLoading, onSignUp }: Props) { const [password, setPassword] = useState({ value: '', touched: false }); const [confirmPassword, setConfirmPassword] = useState({ value: '', touched: false }); const { quizId } = useAppSelector((state) => state.auth.userA); + const { loginUserA: loginA, loginGoogleUser } = useLogin(); + const navigate = useNavigate(); + const location = useLocation(); function handleSubmit(e?: React.FormEvent) { e?.preventDefault(); if (!formIsValid) return; @@ -24,84 +32,110 @@ 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) => { + 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) { + console.log(credentialResponse, 'creds'); + 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 && } -
- + /> + + {/*
+ + {devMode && ( + + )} +
*/} + + ); } diff --git a/src/features/auth/hooks/useLogin.tsx b/src/features/auth/hooks/useLogin.tsx index 1cd7a721..ab091aba 100644 --- a/src/features/auth/hooks/useLogin.tsx +++ b/src/features/auth/hooks/useLogin.tsx @@ -23,10 +23,10 @@ function useLogin() { throw new Error('No credential received from Google'); } const data = await postGoogleLogin(response.credential); - console.log(data); showSuccessToast(`Welcome back, ${data.user.first_name}!`); const { first_name, last_name, email, quiz_id, user_uuid } = data.user; + console.log(quiz_id, 'quiz'); dispatch( loginA({ firstName: first_name, diff --git a/src/shared/hooks/useApiClient.tsx b/src/shared/hooks/useApiClient.tsx index 8c8fd3cf..b0f29734 100644 --- a/src/shared/hooks/useApiClient.tsx +++ b/src/shared/hooks/useApiClient.tsx @@ -149,9 +149,14 @@ function useApiClient() { } async function postGoogleLogin(credential: string) { + if (quizId) { + const response = await apiCall('post', '/auth/google', {}, { credential, quizId }, true); + return response.data; + } const response = await apiCall('post', '/auth/google', {}, { credential }, true); const { access_token, user } = response.data; Cookies.set('accessToken', access_token, { secure: true, sameSite: 'strict' }); + console.log(response.data, 'data'); return response.data; } @@ -159,7 +164,6 @@ function useApiClient() { // Remove the tokens from cookies Cookies.remove('accessToken'); Cookies.remove('refreshToken'); - await apiCall('post', '/logout', {}); } From a285828d93119d9c913e3a440bdd1891b6c5ef96 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Fri, 23 Aug 2024 12:24:24 +0100 Subject: [PATCH 25/61] clean up credentials --- src/features/auth/components/SignUpForm.tsx | 27 ++----------------- .../UserAUnauthorizedPages/LoginPage.tsx | 8 +++--- 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/src/features/auth/components/SignUpForm.tsx b/src/features/auth/components/SignUpForm.tsx index 302d6a75..aa1361c6 100644 --- a/src/features/auth/components/SignUpForm.tsx +++ b/src/features/auth/components/SignUpForm.tsx @@ -41,15 +41,13 @@ function SignUpForm({ isLoading, onSignUp }: Props) { } 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) { - console.log(credentialResponse, 'creds'); navigate(ROUTES.PRE_QUIZ_PAGE); } } catch (error) { @@ -133,28 +131,7 @@ function SignUpForm({ isLoading, onSignUp }: Props) { style={styles.textInput} /> - + {devMode && } {/*
diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 4a35e623..4fd1897b 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -1,6 +1,6 @@ -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; -import Cookies from 'js-cookie'; + import { GoogleOAuthProvider, GoogleLogin } from '@react-oauth/google'; import { CredentialResponse } from '@react-oauth/google'; @@ -10,9 +10,9 @@ import { LoginForm, RequestPasswordResetModal, useLogin, useResetPassword } from import { useMobileView } from 'shared/hooks'; const GOOGLE_CLIENT_ID = process.env.REACT_APP_GOOGLE_CLIENT_ID; -console.log(GOOGLE_CLIENT_ID); function LoginPage() { + const devMode = localStorage.getItem('devMode') === 'true'; const navigate = useNavigate(); const location = useLocation(); const isMobile = useMobileView(); @@ -75,7 +75,7 @@ function LoginPage() { setShowPasswordResetModal(true)} /> - + {devMode && } setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> From ed94941433c3b4c25551b0b598dc60bdddd42952 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:20:09 +0100 Subject: [PATCH 26/61] pushing code so we can test backend --- src/features/auth/components/SignUpForm.tsx | 48 +++++++------------ .../UserAUnauthorizedPages/LoginPage.tsx | 2 +- 2 files changed, 19 insertions(+), 31 deletions(-) diff --git a/src/features/auth/components/SignUpForm.tsx b/src/features/auth/components/SignUpForm.tsx index aa1361c6..02de2f09 100644 --- a/src/features/auth/components/SignUpForm.tsx +++ b/src/features/auth/components/SignUpForm.tsx @@ -131,37 +131,25 @@ function SignUpForm({ isLoading, onSignUp }: Props) { style={styles.textInput} /> - {devMode && } - - {/*
+ {devMode && ( + ( + } + style={{ background: 'white', boxShadow: '0px 2px 3px 0px #0000002B, 0px 0px 3px 0px #00000015', border: 'none' }} + /> + )} + /> + )} + +
- {devMode && ( - - )} -
*/} + {devMode && } +
); diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 4fd1897b..59dd6f61 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -3,7 +3,7 @@ import { useLocation, useNavigate } from 'react-router-dom'; import { GoogleOAuthProvider, GoogleLogin } from '@react-oauth/google'; import { CredentialResponse } from '@react-oauth/google'; - +import { useGoogleLogin } from '@react-oauth/google'; import ROUTES from 'router/RouteConfig'; import { CmBackButton, Page, PageContent } from 'shared/components'; import { LoginForm, RequestPasswordResetModal, useLogin, useResetPassword } from 'features/auth'; From 77fcf7653d8f47901f178a64e1b21ae8de40d5df Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:46:36 +0100 Subject: [PATCH 27/61] upgraded to @react-oauth/google so CI works --- package-lock.json | 15 --------------- package.json | 1 - 2 files changed, 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index b562bb1f..198a81ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,6 @@ "react-chartjs-2": "^5.2.0", "react-device-detect": "^2.2.3", "react-dom": "^18.3.1", - "react-google-login": "^5.2.2", "react-markdown": "^9.0.1", "react-query": "^3.39.3", "react-redux": "^9.1.2", @@ -19559,20 +19558,6 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, - "node_modules/react-google-login": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/react-google-login/-/react-google-login-5.2.2.tgz", - "integrity": "sha512-JUngfvaSMcOuV0lFff7+SzJ2qviuNMQdqlsDJkUM145xkGPVIfqWXq9Ui+2Dr6jdJWH5KYdynz9+4CzKjI5u6g==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dependencies": { - "@types/react": "*", - "prop-types": "^15.6.0" - }, - "peerDependencies": { - "react": "^16 || ^17", - "react-dom": "^16 || ^17" - } - }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", diff --git a/package.json b/package.json index a42ced46..3885c920 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,6 @@ "react-chartjs-2": "^5.2.0", "react-device-detect": "^2.2.3", "react-dom": "^18.3.1", - "react-google-login": "^5.2.2", "react-markdown": "^9.0.1", "react-query": "^3.39.3", "react-redux": "^9.1.2", From 3d8be82a3bedf41bb5c4344149b6a8fce0fbb476 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:53:59 +0100 Subject: [PATCH 28/61] cleaned up code --- src/features/auth/components/SignUpForm.tsx | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/features/auth/components/SignUpForm.tsx b/src/features/auth/components/SignUpForm.tsx index 02de2f09..7a9cfbc1 100644 --- a/src/features/auth/components/SignUpForm.tsx +++ b/src/features/auth/components/SignUpForm.tsx @@ -21,7 +21,6 @@ function SignUpForm({ isLoading, onSignUp }: Props) { 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 { loginUserA: loginA, loginGoogleUser } = useLogin(); const navigate = useNavigate(); const location = useLocation(); @@ -131,21 +130,6 @@ function SignUpForm({ isLoading, onSignUp }: Props) { style={styles.textInput} /> - {devMode && ( - ( - } - style={{ background: 'white', boxShadow: '0px 2px 3px 0px #0000002B, 0px 0px 3px 0px #00000015', border: 'none' }} - /> - )} - /> - )} -
{devMode && } From 76efdcbd819649119caa86166f3927def4f5cfcf Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:57:31 +0100 Subject: [PATCH 29/61] clean up --- src/features/auth/components/SignUpForm.tsx | 3 +-- src/pages/UserAUnauthorizedPages/LoginPage.tsx | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/features/auth/components/SignUpForm.tsx b/src/features/auth/components/SignUpForm.tsx index 7a9cfbc1..2fdc6930 100644 --- a/src/features/auth/components/SignUpForm.tsx +++ b/src/features/auth/components/SignUpForm.tsx @@ -3,7 +3,6 @@ import { useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import ROUTES from 'router/RouteConfig'; import { CmButton2, CmTextInput } from 'shared/components'; -import { useAppSelector } from 'store/hooks'; import { useLogin } from '../hooks'; interface Props { @@ -21,7 +20,7 @@ function SignUpForm({ isLoading, onSignUp }: Props) { const [email, setEmail] = useState({ value: '', touched: false }); const [password, setPassword] = useState({ value: '', touched: false }); const [confirmPassword, setConfirmPassword] = useState({ value: '', touched: false }); - const { loginUserA: loginA, loginGoogleUser } = useLogin(); + const { loginGoogleUser } = useLogin(); const navigate = useNavigate(); const location = useLocation(); function handleSubmit(e?: React.FormEvent) { diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 59dd6f61..438be4ba 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -3,7 +3,6 @@ import { useLocation, useNavigate } from 'react-router-dom'; import { GoogleOAuthProvider, GoogleLogin } from '@react-oauth/google'; import { CredentialResponse } from '@react-oauth/google'; -import { useGoogleLogin } from '@react-oauth/google'; import ROUTES from 'router/RouteConfig'; import { CmBackButton, Page, PageContent } from 'shared/components'; import { LoginForm, RequestPasswordResetModal, useLogin, useResetPassword } from 'features/auth'; From 7ea84187d95426d2cd59556572d890c3f92cf2f7 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Fri, 23 Aug 2024 16:02:35 +0100 Subject: [PATCH 30/61] clean up --- src/features/auth/components/SignUpForm.tsx | 2 +- src/pages/UserAUnauthorizedPages/LoginPage.tsx | 2 +- src/shared/hooks/useApiClient.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/features/auth/components/SignUpForm.tsx b/src/features/auth/components/SignUpForm.tsx index 2fdc6930..59d61e42 100644 --- a/src/features/auth/components/SignUpForm.tsx +++ b/src/features/auth/components/SignUpForm.tsx @@ -131,7 +131,7 @@ function SignUpForm({ isLoading, onSignUp }: Props) {
- {devMode && } + {devMode && handleGoogleError} />}
diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 438be4ba..72067808 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -74,7 +74,7 @@ function LoginPage() { setShowPasswordResetModal(true)} /> - {devMode && } + {devMode && handleGoogleError} />} setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> diff --git a/src/shared/hooks/useApiClient.tsx b/src/shared/hooks/useApiClient.tsx index b0f29734..fd070b1a 100644 --- a/src/shared/hooks/useApiClient.tsx +++ b/src/shared/hooks/useApiClient.tsx @@ -154,7 +154,7 @@ function useApiClient() { return response.data; } const response = await apiCall('post', '/auth/google', {}, { credential }, true); - const { access_token, user } = response.data; + const { access_token } = response.data; Cookies.set('accessToken', access_token, { secure: true, sameSite: 'strict' }); console.log(response.data, 'data'); return response.data; From 3d8672ed0981418bed59b013b180e0d6773da1d3 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Sat, 24 Aug 2024 10:02:34 +0100 Subject: [PATCH 31/61] clean up --- src/features/auth/components/LoginForm.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/features/auth/components/LoginForm.tsx b/src/features/auth/components/LoginForm.tsx index ba27cb16..98233740 100644 --- a/src/features/auth/components/LoginForm.tsx +++ b/src/features/auth/components/LoginForm.tsx @@ -8,9 +8,7 @@ interface Props { 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 +34,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' }} /> - )} + )} */}
); From c08c41bb780592715b332e45db7734711b748c9a Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Sat, 24 Aug 2024 10:51:01 +0100 Subject: [PATCH 32/61] changed style of button on login page to demonstrate it can be done --- src/pages/UserAUnauthorizedPages/LoginPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 72067808..39fa5e1f 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -74,7 +74,7 @@ function LoginPage() { setShowPasswordResetModal(true)} /> - {devMode && handleGoogleError} />} + {devMode && handleGoogleError} shape="pill" logo_alignment="left" theme="outline" />} setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> From 284160a25b7dc3d07edb84aeb0dbdd5cdce5459b Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Sat, 24 Aug 2024 11:10:37 +0100 Subject: [PATCH 33/61] pushing sign up button chnages --- src/features/auth/components/SignUpForm.tsx | 2 +- src/pages/UserAUnauthorizedPages/LoginPage.tsx | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/features/auth/components/SignUpForm.tsx b/src/features/auth/components/SignUpForm.tsx index 59d61e42..65396c5e 100644 --- a/src/features/auth/components/SignUpForm.tsx +++ b/src/features/auth/components/SignUpForm.tsx @@ -131,7 +131,7 @@ function SignUpForm({ isLoading, onSignUp }: Props) {
- {devMode && handleGoogleError} />} + {devMode && handleGoogleError} text="continue_with" shape="pill" logo_alignment="left" theme="outline" />}
diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 39fa5e1f..0a04260a 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -73,9 +73,7 @@ function LoginPage() { Climate Mind Logo setShowPasswordResetModal(true)} /> - - {devMode && handleGoogleError} shape="pill" logo_alignment="left" theme="outline" />} - +
{devMode && handleGoogleError} shape="pill" logo_alignment="left" theme="outline" />}
setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> From 7204e0a2838b4680c8f05d8b9680b843464b1c17 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Sun, 25 Aug 2024 09:30:38 +0100 Subject: [PATCH 34/61] clean up --- src/features/auth/hooks/useLogin.tsx | 2 +- src/pages/UserAUnauthorizedPages/LoginPage.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/features/auth/hooks/useLogin.tsx b/src/features/auth/hooks/useLogin.tsx index ab091aba..d132f676 100644 --- a/src/features/auth/hooks/useLogin.tsx +++ b/src/features/auth/hooks/useLogin.tsx @@ -26,7 +26,7 @@ function useLogin() { showSuccessToast(`Welcome back, ${data.user.first_name}!`); const { first_name, last_name, email, quiz_id, user_uuid } = data.user; - console.log(quiz_id, 'quiz'); + dispatch( loginA({ firstName: first_name, diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 0a04260a..98c42d3e 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -43,11 +43,11 @@ function LoginPage() { } const handleGoogleSuccess = async (credentialResponse: CredentialResponse) => { - console.log('Google login success, credential:', credentialResponse); + // console.log('Google login success, credential:', credentialResponse); setIsLoading(true); try { const isSuccessful = await loginGoogleUser(credentialResponse); - console.log('loginGoogleUser result:', isSuccessful); + // console.log('loginGoogleUser result:', isSuccessful); if (isSuccessful) { navigateAfterLogin(); } else if (!isSuccessful) { From 0cbe4450e9ab9de8db22c04ad4a711b8fd4537cc Mon Sep 17 00:00:00 2001 From: Svenstar74 Date: Sun, 25 Aug 2024 17:00:45 +0200 Subject: [PATCH 35/61] small cleanup --- src/features/auth/components/LoginForm.tsx | 1 - src/features/auth/components/SignUpForm.tsx | 12 ++++++++---- src/features/auth/hooks/useLogin.tsx | 6 ++++-- src/pages/UserAUnauthorizedPages/LoginPage.tsx | 15 ++++++++------- src/shared/hooks/useApiClient.tsx | 1 + 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/features/auth/components/LoginForm.tsx b/src/features/auth/components/LoginForm.tsx index 98233740..83462e56 100644 --- a/src/features/auth/components/LoginForm.tsx +++ b/src/features/auth/components/LoginForm.tsx @@ -5,7 +5,6 @@ interface Props { isLoading: boolean; onLogin: (email: string, password: string) => void; onForgotPasswordClick: () => void; - handleGoogleAuth?: () => void; } function LoginForm({ isLoading, onLogin, onForgotPasswordClick }: Props) { diff --git a/src/features/auth/components/SignUpForm.tsx b/src/features/auth/components/SignUpForm.tsx index 65396c5e..b54dd2db 100644 --- a/src/features/auth/components/SignUpForm.tsx +++ b/src/features/auth/components/SignUpForm.tsx @@ -1,28 +1,32 @@ -import { CredentialResponse, GoogleLogin, GoogleOAuthProvider } from '@react-oauth/google'; 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; + interface Props { isLoading: boolean; onSignUp: (firstname: string, lastname: string, email: string, password: string) => void; } -const GOOGLE_CLIENT_ID = process.env.REACT_APP_GOOGLE_CLIENT_ID; 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(); - const navigate = useNavigate(); - const location = useLocation(); + function handleSubmit(e?: React.FormEvent) { e?.preventDefault(); if (!formIsValid) return; diff --git a/src/features/auth/hooks/useLogin.tsx b/src/features/auth/hooks/useLogin.tsx index d132f676..094ab632 100644 --- a/src/features/auth/hooks/useLogin.tsx +++ b/src/features/auth/hooks/useLogin.tsx @@ -1,7 +1,9 @@ +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'; -import { CredentialResponse } from '@react-oauth/google'; + function useLogin() { const dispatch = useAppDispatch(); @@ -16,12 +18,12 @@ function useLogin() { * Email and password are required. * @returns true if login was successful, false otherwise */ - async function loginGoogleUser(response: CredentialResponse): Promise { try { if (!response.credential) { throw new Error('No credential received from Google'); } + const data = await postGoogleLogin(response.credential); showSuccessToast(`Welcome back, ${data.user.first_name}!`); diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 98c42d3e..a2b44c10 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -1,8 +1,7 @@ import { useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; +import { CredentialResponse, GoogleOAuthProvider, GoogleLogin } from '@react-oauth/google'; -import { GoogleOAuthProvider, GoogleLogin } from '@react-oauth/google'; -import { CredentialResponse } from '@react-oauth/google'; import ROUTES from 'router/RouteConfig'; import { CmBackButton, Page, PageContent } from 'shared/components'; import { LoginForm, RequestPasswordResetModal, useLogin, useResetPassword } from 'features/auth'; @@ -12,9 +11,11 @@ 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(); @@ -29,11 +30,6 @@ function LoginPage() { setIsLoading(false); } - async function handlePasswordReset(email: string) { - setShowPasswordResetModal(false); - await sendPasswordResetLink(email); - } - function navigateAfterLogin() { if (location.state && 'from' in location.state) { navigate(location.state.from); @@ -42,6 +38,11 @@ function LoginPage() { } } + async function handlePasswordReset(email: string) { + setShowPasswordResetModal(false); + await sendPasswordResetLink(email); + } + const handleGoogleSuccess = async (credentialResponse: CredentialResponse) => { // console.log('Google login success, credential:', credentialResponse); setIsLoading(true); diff --git a/src/shared/hooks/useApiClient.tsx b/src/shared/hooks/useApiClient.tsx index fd070b1a..0d86ddbb 100644 --- a/src/shared/hooks/useApiClient.tsx +++ b/src/shared/hooks/useApiClient.tsx @@ -153,6 +153,7 @@ function useApiClient() { const response = await apiCall('post', '/auth/google', {}, { credential, quizId }, true); return response.data; } + const response = await apiCall('post', '/auth/google', {}, { credential }, true); const { access_token } = response.data; Cookies.set('accessToken', access_token, { secure: true, sameSite: 'strict' }); From 5f761a12428b48ec3dc6a1584f821f0474cb1c56 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Tue, 27 Aug 2024 10:13:10 +0100 Subject: [PATCH 36/61] chore: Clean up nginx.config file (#654) --- nginx.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx.config b/nginx.config index 3ae5ed51..2f1bb174 100644 --- a/nginx.config +++ b/nginx.config @@ -9,11 +9,11 @@ server { try_files $$uri /index.html; } - add_header Content-Security-Policy "default-src 'self'; frame-src https://www.google.com/; script-src 'self' https://www.googletagmanager.com/ https://*.google-analytics.com https://*.analytics.google.com https://www.google.com/ https://www.gstatic.com/ 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://*.typekit.net https://fonts.googleapis.com; img-src * www.googletagmanager.com 'self' data: https; font-src 'self' *.typekit.net fonts.googleapis.com fonts.gstatic.com; connect-src 'self' https://*.okta.com https://app-backend-test-001.azurewebsites.net https://app-backend-prod-001.azurewebsites.net https://sentry.io https://o1287611.ingest.sentry.io/api/6526369/envelope/?sentry_key=b0ca2fb00555461ba86f659a99cceb37&sentry_version=7 https://o1287611.ingest.sentry.io/api/6526369/security/?sentry_key=b0ca2fb00555461ba86f659a99cceb37; report-uri https://o1287611.ingest.sentry.io/api/6526369/security/?sentry_key=b0ca2fb00555461ba86f659a99cceb37;"; add_header Referrer-Policy "no-referrer, strict-origin-when-cross-origin"; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains"; add_header X-Content-Type-Options nosniff; add_header X-Frame-Options DENY; add_header X-XSS-Protection "1; mode=block"; add_header Feature-Policy "accelerometer 'none'; camera 'none'; microphone 'none'"; + } From 0b070a719715caa2c036d8f9b141c4d76b61f536 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Tue, 27 Aug 2024 10:21:58 +0100 Subject: [PATCH 37/61] chore: Add Content-Security-Policy header to nginx.config (#655) --- nginx.config | 1 + 1 file changed, 1 insertion(+) diff --git a/nginx.config b/nginx.config index 2f1bb174..1fa863c2 100644 --- a/nginx.config +++ b/nginx.config @@ -15,5 +15,6 @@ server { add_header X-Frame-Options DENY; add_header X-XSS-Protection "1; mode=block"; add_header Feature-Policy "accelerometer 'none'; camera 'none'; microphone 'none'"; + add_header Content-Security-Policy "script-src 'self' https://www.googletagmanager.com/ https://*.google-analytics.com https://*.analytics.google.com https://www.google.com/ https://www.gstatic.com/ https://accounts.google.com 'unsafe-inline' 'unsafe-eval'"; } From 352034f5b61bb537781849ccea08de6858efa1ac Mon Sep 17 00:00:00 2001 From: Svenstar74 Date: Tue, 27 Aug 2024 11:27:28 +0200 Subject: [PATCH 38/61] chore: Update Content-Security-Policy header in nginx.config --- nginx.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx.config b/nginx.config index 1fa863c2..539977bf 100644 --- a/nginx.config +++ b/nginx.config @@ -15,6 +15,6 @@ server { add_header X-Frame-Options DENY; add_header X-XSS-Protection "1; mode=block"; add_header Feature-Policy "accelerometer 'none'; camera 'none'; microphone 'none'"; - add_header Content-Security-Policy "script-src 'self' https://www.googletagmanager.com/ https://*.google-analytics.com https://*.analytics.google.com https://www.google.com/ https://www.gstatic.com/ https://accounts.google.com 'unsafe-inline' 'unsafe-eval'"; + add_header Content-Security-Policy "script-src 'self' https://www.googletagmanager.com/ https://accounts.google.com/gsi/client https://*.google-analytics.com https://*.analytics.google.com https://www.google.com/ https://www.gstatic.com/ https://accounts.google.com 'unsafe-inline' 'unsafe-eval'"; } From 1b3dfc3a54c665e16cc9028415625c4624df2efd Mon Sep 17 00:00:00 2001 From: Svenstar74 Date: Tue, 27 Aug 2024 11:32:11 +0200 Subject: [PATCH 39/61] chore: Update Content-Security-Policy header in nginx.config --- src/pages/UserAUnauthorizedPages/LoginPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index a2b44c10..6f9f4492 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -63,7 +63,7 @@ function LoginPage() { const handleGoogleError = (error: any) => { console.error('Google Login Failed:', error); }; - +console.log("GOOGLE_CLIENT_ID", GOOGLE_CLIENT_ID) return ( From 22d289cf04521f7e0e3968b79e4567cf07f14cd3 Mon Sep 17 00:00:00 2001 From: Svenstar74 Date: Tue, 27 Aug 2024 11:48:54 +0200 Subject: [PATCH 40/61] chore: Update environment variables for Google OAuth --- .env.development | 1 + .env.production | 1 + 2 files changed, 2 insertions(+) diff --git a/.env.development b/.env.development index 0e420066..9e9356a7 100644 --- a/.env.development +++ b/.env.development @@ -1,2 +1,3 @@ REACT_APP_API_URL=https://app-backend-test-001.azurewebsites.net REACT_APP_SENTRY_DSN=https://b0ca2fb00555461ba86f659a99cceb37@o1287611.ingest.sentry.io/6526369 +REACT_APP_GOOGLE_CLIENT_ID=40848962667-vu42kp42ba16q3mc1ah8l8rmm028jskf.apps.googleusercontent.com diff --git a/.env.production b/.env.production index 01b9acff..ef7b8dec 100644 --- a/.env.production +++ b/.env.production @@ -1,2 +1,3 @@ REACT_APP_API_URL=https://app-backend-prod-001.azurewebsites.net REACT_APP_SENTRY_DSN=https://b0ca2fb00555461ba86f659a99cceb37@o1287611.ingest.sentry.io/6526369 +REACT_APP_GOOGLE_CLIENT_ID=40848962667-vu42kp42ba16q3mc1ah8l8rmm028jskf.apps.googleusercontent.com From 708290da0d9c828613984827c4178ba39d8a9caa Mon Sep 17 00:00:00 2001 From: Svenstar74 Date: Tue, 27 Aug 2024 11:50:33 +0200 Subject: [PATCH 41/61] chore: Update serverdata.sh to include additional environment variable in index.html --- serverdata.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/serverdata.sh b/serverdata.sh index 0f0f9001..eadfc0e4 100644 --- a/serverdata.sh +++ b/serverdata.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash INDEXPATH=/usr/share/nginx/html/index.html -JSON_STRING=$( jq -n --arg apiUrl "$REACT_APP_API_URL" '{REACT_APP_API_URL: $apiUrl}' ) -sed -i 's/__SERVERDATA__/$JSON_STRING/g' $INDEXPATH -#awk '{sub("__SERVERDATA__",$JSON_STRING)}1' $1 > temp.txt && mv temp.txt $1 + +# Create JSON_STRING with both environment variables +JSON_STRING=$( jq -n --arg apiUrl "$REACT_APP_API_URL" --arg googleClientId "$REACT_APP_GOOGLE_CLIENT_ID" \ + '{REACT_APP_API_URL: $apiUrl, REACT_APP_GOOGLE_CLIENT_ID: $googleClientId}' ) + +# Replace the placeholder with the JSON_STRING in the index.html file +sed -i "s/__SERVERDATA__/$JSON_STRING/g" $INDEXPATH From 3765791f4bce879d5a92ef679cb7bb2dcf4b00a0 Mon Sep 17 00:00:00 2001 From: Svenstar74 Date: Tue, 27 Aug 2024 11:57:14 +0200 Subject: [PATCH 42/61] Remove log statement --- src/pages/UserAUnauthorizedPages/LoginPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 6f9f4492..a2b44c10 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -63,7 +63,7 @@ function LoginPage() { const handleGoogleError = (error: any) => { console.error('Google Login Failed:', error); }; -console.log("GOOGLE_CLIENT_ID", GOOGLE_CLIENT_ID) + return ( From f5f9c846e55dc31aa4542955e18a7c6595d1a8f1 Mon Sep 17 00:00:00 2001 From: Svenstar74 Date: Tue, 27 Aug 2024 11:58:24 +0200 Subject: [PATCH 43/61] chore: Update Content-Security-Policy header in nginx.config --- nginx.config | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nginx.config b/nginx.config index 539977bf..1de21dee 100644 --- a/nginx.config +++ b/nginx.config @@ -9,12 +9,11 @@ server { try_files $$uri /index.html; } + add_header Content-Security-Policy "default-src 'self'; frame-src https://www.google.com/; script-src 'self' https://www.googletagmanager.com/ https://*.google-analytics.com https://*.analytics.google.com https://www.google.com/ https://www.gstatic.com/ https://accounts.google.com 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://*.typekit.net https://fonts.googleapis.com; img-src * www.googletagmanager.com 'self' data: https; font-src 'self' *.typekit.net fonts.googleapis.com fonts.gstatic.com; connect-src 'self' https://*.okta.com https://app-backend-test-001.azurewebsites.net https://app-backend-prod-001.azurewebsites.net https://sentry.io https://o1287611.ingest.sentry.io/api/6526369/envelope/?sentry_key=b0ca2fb00555461ba86f659a99cceb37&sentry_version=7 https://o1287611.ingest.sentry.io/api/6526369/security/?sentry_key=b0ca2fb00555461ba86f659a99cceb37; report-uri https://o1287611.ingest.sentry.io/api/6526369/security/?sentry_key=b0ca2fb00555461ba86f659a99cceb37;"; add_header Referrer-Policy "no-referrer, strict-origin-when-cross-origin"; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains"; add_header X-Content-Type-Options nosniff; add_header X-Frame-Options DENY; add_header X-XSS-Protection "1; mode=block"; add_header Feature-Policy "accelerometer 'none'; camera 'none'; microphone 'none'"; - add_header Content-Security-Policy "script-src 'self' https://www.googletagmanager.com/ https://accounts.google.com/gsi/client https://*.google-analytics.com https://*.analytics.google.com https://www.google.com/ https://www.gstatic.com/ https://accounts.google.com 'unsafe-inline' 'unsafe-eval'"; - } From 96256f5744b5c3e91431307db588a5b21d863c2c Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:25:44 +0100 Subject: [PATCH 44/61] Update Content-Security-Policy header in nginx.config --- nginx.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx.config b/nginx.config index 1de21dee..0e9eb422 100644 --- a/nginx.config +++ b/nginx.config @@ -9,7 +9,7 @@ server { try_files $$uri /index.html; } - add_header Content-Security-Policy "default-src 'self'; frame-src https://www.google.com/; script-src 'self' https://www.googletagmanager.com/ https://*.google-analytics.com https://*.analytics.google.com https://www.google.com/ https://www.gstatic.com/ https://accounts.google.com 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://*.typekit.net https://fonts.googleapis.com; img-src * www.googletagmanager.com 'self' data: https; font-src 'self' *.typekit.net fonts.googleapis.com fonts.gstatic.com; connect-src 'self' https://*.okta.com https://app-backend-test-001.azurewebsites.net https://app-backend-prod-001.azurewebsites.net https://sentry.io https://o1287611.ingest.sentry.io/api/6526369/envelope/?sentry_key=b0ca2fb00555461ba86f659a99cceb37&sentry_version=7 https://o1287611.ingest.sentry.io/api/6526369/security/?sentry_key=b0ca2fb00555461ba86f659a99cceb37; report-uri https://o1287611.ingest.sentry.io/api/6526369/security/?sentry_key=b0ca2fb00555461ba86f659a99cceb37;"; + add_header Content-Security-Policy "default-src 'self'; frame-src https://www.google.com/ https://accounts.google.com/; script-src 'self' https://www.googletagmanager.com/ https://*.google-analytics.com https://*.analytics.google.com https://www.google.com/ https://www.gstatic.com/ https://accounts.google.com 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://*.typekit.net https://fonts.googleapis.com https://accounts.google.com; style-src-elem 'self' 'unsafe-inline' https://*.typekit.net https://fonts.googleapis.com https://accounts.google.com; img-src * www.googletagmanager.com 'self' data: https; font-src 'self' *.typekit.net fonts.googleapis.com fonts.gstatic.com; connect-src 'self' https://*.okta.com https://app-backend-test-001.azurewebsites.net https://app-backend-prod-001.azurewebsites.net https://sentry.io https://o1287611.ingest.sentry.io/api/6526369/envelope/?sentry_key=b0ca2fb00555461ba86f659a99cceb37&sentry_version=7 https://o1287611.ingest.sentry.io/api/6526369/security/?sentry_key=b0ca2fb00555461ba86f659a99cceb37; report-uri https://o1287611.ingest.sentry.io/api/6526369/security/?sentry_key=b0ca2fb00555461ba86f659a99cceb37;"; add_header Referrer-Policy "no-referrer, strict-origin-when-cross-origin"; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains"; add_header X-Content-Type-Options nosniff; From 3fedd9b47773f3953075aba5e3decd4b301cbdb6 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:31:28 +0100 Subject: [PATCH 45/61] clean up --- src/shared/hooks/useApiClient.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/shared/hooks/useApiClient.tsx b/src/shared/hooks/useApiClient.tsx index 0d86ddbb..42b8db24 100644 --- a/src/shared/hooks/useApiClient.tsx +++ b/src/shared/hooks/useApiClient.tsx @@ -157,7 +157,6 @@ function useApiClient() { 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; } From 01b8381d7b8e8f5f9fd2fa78a837203b5d58fe56 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Tue, 3 Sep 2024 09:52:58 +0100 Subject: [PATCH 46/61] Closes [661] - Customize the google login button (#662) * Customized the google button * login form styled * created custom google login component * cleaned and refactored * added devmode * cleanup --------- Co-authored-by: Svenstar74 --- src/App.tsx | 19 +- src/features/auth/components/GoogleLogin.tsx | 64 +++++++ src/features/auth/components/LoginForm.tsx | 10 - src/features/auth/components/SignUpForm.tsx | 181 +++++++----------- src/features/auth/hooks/useLogin.tsx | 4 +- .../UserAUnauthorizedPages/LoginPage.tsx | 52 ++--- .../UserAUnauthorizedPages/SignUpPage.tsx | 27 ++- 7 files changed, 183 insertions(+), 174 deletions(-) create mode 100644 src/features/auth/components/GoogleLogin.tsx diff --git a/src/App.tsx b/src/App.tsx index a5529a5b..9133dc5b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { RouterProvider } from 'react-router-dom'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import * as Sentry from '@sentry/react'; +import { GoogleOAuthProvider } from '@react-oauth/google'; import './global.css'; import router from './router/Router'; @@ -12,15 +13,19 @@ import { store } from 'store/store'; export const queryClient = new QueryClient(); const App = () => { + const GOOGLE_CLIENT_ID = process.env.REACT_APP_GOOGLE_CLIENT_ID; + return ( - - - - - - - + + + + + + + + + ); }; diff --git a/src/features/auth/components/GoogleLogin.tsx b/src/features/auth/components/GoogleLogin.tsx new file mode 100644 index 00000000..290ef6e8 --- /dev/null +++ b/src/features/auth/components/GoogleLogin.tsx @@ -0,0 +1,64 @@ +import { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { CredentialResponse } from '@react-oauth/google'; + +import ROUTES from 'router/RouteConfig'; +import { CmButton2 } from 'shared/components'; +import { useLogin } from '../hooks'; + +interface Props { + navigateAfterLogin: () => void; + text?: string; +} + +function GoogleLogin({ navigateAfterLogin, text }: Props) { + const GOOGLE_CLIENT_ID = process.env.REACT_APP_GOOGLE_CLIENT_ID; + + const navigate = useNavigate(); + const { loginGoogleUser } = useLogin(); + const [isLoading, setIsLoading] = useState(false); + + async function handleGoogleSuccess(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 ( + } + style={{ background: 'white', boxShadow: '0px 2px 3px 0px #0000002B, 0px 0px 3px 0px #00000015', border: 'none' }} + /> + ); +} + +export default GoogleLogin; diff --git a/src/features/auth/components/LoginForm.tsx b/src/features/auth/components/LoginForm.tsx index 83462e56..bd47635e 100644 --- a/src/features/auth/components/LoginForm.tsx +++ b/src/features/auth/components/LoginForm.tsx @@ -32,16 +32,6 @@ function LoginForm({ isLoading, onLogin, onForgotPasswordClick }: Props) {
- - {/* {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 b54dd2db..c26f55a3 100644 --- a/src/features/auth/components/SignUpForm.tsx +++ b/src/features/auth/components/SignUpForm.tsx @@ -1,12 +1,5 @@ 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; interface Props { isLoading: boolean; @@ -14,18 +7,11 @@ interface Props { } 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(); @@ -34,111 +20,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 ( - -
- 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" />} -
- -
+
+ 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} + /> + +
+ +
+ ); } diff --git a/src/features/auth/hooks/useLogin.tsx b/src/features/auth/hooks/useLogin.tsx index 094ab632..b70a7469 100644 --- a/src/features/auth/hooks/useLogin.tsx +++ b/src/features/auth/hooks/useLogin.tsx @@ -20,11 +20,11 @@ function useLogin() { */ async function loginGoogleUser(response: CredentialResponse): Promise { try { - if (!response.credential) { + if (!response) { throw new Error('No credential received from Google'); } - const data = await postGoogleLogin(response.credential); + const data = await postGoogleLogin(response.toString()); showSuccessToast(`Welcome back, ${data.user.first_name}!`); const { first_name, last_name, email, quiz_id, user_uuid } = data.user; diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index a2b44c10..79f234e1 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -1,23 +1,20 @@ import { useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; -import { CredentialResponse, GoogleOAuthProvider, GoogleLogin } 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); @@ -43,42 +40,23 @@ function LoginPage() { await sendPasswordResetLink(email); } - 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); - }; - - const handleGoogleError = (error: any) => { - console.error('Google Login Failed:', error); - }; - return ( - - - - {isMobile && navigate(-1)} style={styles.backButton} />} + + + {isMobile && navigate(-1)} style={styles.backButton} />} - Climate Mind Logo - Climate Mind Logo + Climate Mind Logo + Climate Mind Logo +
setShowPasswordResetModal(true)} /> -
{devMode && handleGoogleError} shape="pill" logo_alignment="left" theme="outline" />}
- setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> - - - +
+ {devMode && } +
+ + setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> +
+
); } diff --git a/src/pages/UserAUnauthorizedPages/SignUpPage.tsx b/src/pages/UserAUnauthorizedPages/SignUpPage.tsx index 66b3493d..82a21652 100644 --- a/src/pages/UserAUnauthorizedPages/SignUpPage.tsx +++ b/src/pages/UserAUnauthorizedPages/SignUpPage.tsx @@ -1,20 +1,24 @@ import { v4 as uuidv4 } from 'uuid'; import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import ROUTES from '../../router/RouteConfig'; import { analyticsService, RegistrationPageOpenEvent } from 'services'; import { useAppSelector } from 'store/hooks'; import { CmTypography, Page, PageContent } from 'shared/components'; import { SignUpForm, useSignUp } from 'features/auth'; +import GoogleLogin from 'features/auth/components/GoogleLogin'; function SignUpPage() { + const devMode = localStorage.getItem('devMode') === 'true'; + const signUpId = uuidv4(); const navigate = useNavigate(); + const location = useLocation(); const [isLoading, setIsLoading] = useState(false); const { signUp } = useSignUp(); - const { sessionId, quizId } = useAppSelector(state => state.auth.userA); + const { sessionId, quizId } = useAppSelector((state) => state.auth.userA); async function signUpHandler(firstname: string, lastname: string, email: string, password: string) { setIsLoading(true); @@ -22,7 +26,13 @@ function SignUpPage() { if (success) navigate(ROUTES.CLIMATE_FEED_PAGE); setIsLoading(false); } - + function navigateAfterLogin() { + if (location.state && 'from' in location.state) { + navigate(location.state.from); + } else { + navigate(ROUTES.CLIMATE_FEED_PAGE); + } + } useEffect(() => { if (sessionId) analyticsService.postEvent(RegistrationPageOpenEvent, signUpId); }, [sessionId, signUpId]); @@ -30,12 +40,19 @@ function SignUpPage() { return ( - Create a Climate Mind account + + Create a Climate Mind account + + Save your results, see your climate topics, and start talking. - +
+ +
+ {devMode && } +
); From e8d07e6725efe0dcfbd1eac2ad1dc769ffe34d17 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Tue, 3 Sep 2024 19:51:46 +0100 Subject: [PATCH 47/61] chore: Update Content-Security-Policy header in nginx.config (#664) --- nginx.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nginx.config b/nginx.config index 0e9eb422..9d32d1c8 100644 --- a/nginx.config +++ b/nginx.config @@ -9,7 +9,7 @@ server { try_files $$uri /index.html; } - add_header Content-Security-Policy "default-src 'self'; frame-src https://www.google.com/ https://accounts.google.com/; script-src 'self' https://www.googletagmanager.com/ https://*.google-analytics.com https://*.analytics.google.com https://www.google.com/ https://www.gstatic.com/ https://accounts.google.com 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://*.typekit.net https://fonts.googleapis.com https://accounts.google.com; style-src-elem 'self' 'unsafe-inline' https://*.typekit.net https://fonts.googleapis.com https://accounts.google.com; img-src * www.googletagmanager.com 'self' data: https; font-src 'self' *.typekit.net fonts.googleapis.com fonts.gstatic.com; connect-src 'self' https://*.okta.com https://app-backend-test-001.azurewebsites.net https://app-backend-prod-001.azurewebsites.net https://sentry.io https://o1287611.ingest.sentry.io/api/6526369/envelope/?sentry_key=b0ca2fb00555461ba86f659a99cceb37&sentry_version=7 https://o1287611.ingest.sentry.io/api/6526369/security/?sentry_key=b0ca2fb00555461ba86f659a99cceb37; report-uri https://o1287611.ingest.sentry.io/api/6526369/security/?sentry_key=b0ca2fb00555461ba86f659a99cceb37;"; + add_header Content-Security-Policy "default-src 'self'; frame-src https://www.google.com/ https://accounts.google.com/; script-src 'self' https://www.googletagmanager.com/ https://*.google-analytics.com https://*.analytics.google.com https://www.google.com/ https://www.gstatic.com/ https://accounts.google.com 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://*.typekit.net https://fonts.googleapis.com https://accounts.google.com; style-src-elem 'self' 'unsafe-inline' https://*.typekit.net https://fonts.googleapis.com https://accounts.google.com; img-src * www.googletagmanager.com 'self' data: https; font-src 'self' *.typekit.net fonts.googleapis.com fonts.gstatic.com; connect-src 'self' https://accounts.google.com https://accounts.google.com https://*.okta.com https://app-backend-test-001.azurewebsites.net https://app-backend-prod-001.azurewebsites.net https://sentry.io https://o1287611.ingest.sentry.io/api/6526369/envelope/?sentry_key=b0ca2fb00555461ba86f659a99cceb37&sentry_version=7 https://o1287611.ingest.sentry.io/api/6526369/security/?sentry_key=b0ca2fb00555461ba86f659a99cceb37; report-uri https://o1287611.ingest.sentry.io/api/6526369/security/?sentry_key=b0ca2fb00555461ba86f659a99cceb37;"; add_header Referrer-Policy "no-referrer, strict-origin-when-cross-origin"; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains"; add_header X-Content-Type-Options nosniff; From 0fb53c6b945df4a763d4d85b1afeb81f56b9f165 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Thu, 5 Sep 2024 08:10:08 +0100 Subject: [PATCH 48/61] Closes [665] - Create userb google login and sign in (#667) * feat: Add support for logging in with Google for User B * adding changes to logic * Added logic for userb if user does not exist * feat: Update Google login component for User B journey * logic implemented for both userb sign in and login * cleanup --------- Co-authored-by: Svenstar74 --- src/features/auth/components/GoogleLogin.tsx | 39 ++++++++++++---- src/features/auth/hooks/useLogin.tsx | 43 ++++++++++++++--- src/pages/UserBPages/UserBLoginPage.tsx | 21 +++++++-- .../UserBPages/UserBSharedSuccessPage.tsx | 25 ++++------ .../UserBPages/UserBSharedSummaryPage.tsx | 46 +++++++++++-------- src/pages/UserBPages/UserBSignUpPage.tsx | 27 ++++++++--- src/shared/hooks/useApiClient.tsx | 2 +- 7 files changed, 140 insertions(+), 63 deletions(-) diff --git a/src/features/auth/components/GoogleLogin.tsx b/src/features/auth/components/GoogleLogin.tsx index 290ef6e8..630997e9 100644 --- a/src/features/auth/components/GoogleLogin.tsx +++ b/src/features/auth/components/GoogleLogin.tsx @@ -1,10 +1,11 @@ import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { CredentialResponse } from '@react-oauth/google'; import ROUTES from 'router/RouteConfig'; import { CmButton2 } from 'shared/components'; import { useLogin } from '../hooks'; +import useToastMessage from '../../../shared/hooks/useToastMessage'; interface Props { navigateAfterLogin: () => void; @@ -15,23 +16,45 @@ function GoogleLogin({ navigateAfterLogin, text }: Props) { const GOOGLE_CLIENT_ID = process.env.REACT_APP_GOOGLE_CLIENT_ID; const navigate = useNavigate(); - const { loginGoogleUser } = useLogin(); + const location = useLocation(); + const { conversationId } = useParams(); + + const { showErrorToast } = useToastMessage(); + const { loginGoogleUserA, loginGoogleUserB } = useLogin(); + const [isLoading, setIsLoading] = useState(false); async function handleGoogleSuccess(credentialResponse: CredentialResponse) { setIsLoading(true); try { - const isSuccessful = await loginGoogleUser(credentialResponse); - if (isSuccessful) { - navigateAfterLogin(); - } else if (!isSuccessful) { - navigate(ROUTES.PRE_QUIZ_PAGE); + if (conversationId) { + const isSuccessful = await loginGoogleUserB(credentialResponse); + if (isSuccessful && location.pathname === ROUTES.USERB_LOGIN_PAGE + '/' + conversationId) { + navigate(ROUTES.USERB_CORE_VALUES_PAGE + '/' + conversationId); + } else if (isSuccessful && location.pathname === ROUTES.USERB_CORE_VALUES_PAGE + '/' + conversationId) { + } else if (!isSuccessful) { + navigate(ROUTES.USERB_HOW_CM_WORKS_PAGE + '/' + conversationId); + showErrorToast('Please Do The Quiz First'); + } else if (location.pathname === ROUTES.USERB_SIGN_UP_PAGE + '/' + conversationId) { + const isSuccessful = await loginGoogleUserA(credentialResponse); + if (isSuccessful) { + navigate(ROUTES.CLIMATE_FEED_PAGE); + } + } + } else { + const isSuccessful = await loginGoogleUserA(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 */ diff --git a/src/features/auth/hooks/useLogin.tsx b/src/features/auth/hooks/useLogin.tsx index b70a7469..ca254ee8 100644 --- a/src/features/auth/hooks/useLogin.tsx +++ b/src/features/auth/hooks/useLogin.tsx @@ -1,15 +1,16 @@ import { CredentialResponse } from '@react-oauth/google'; -import { useAppDispatch } from 'store/hooks'; +import { useAppDispatch, useAppSelector } from 'store/hooks'; import { useApiClient, useToastMessage } from 'shared/hooks'; import { loginUserA as loginA, loginUserB as loginB } from '../state/authSlice'; function useLogin() { const dispatch = useAppDispatch(); - + const quizIdB = useAppSelector((state) => state.auth.userB.quizId); + const quizIdA = useAppSelector((state) => state.auth.userA.quizId); + const apiClient = useApiClient(); const { showSuccessToast, showErrorToast } = useToastMessage(); - const { postGoogleLogin } = useApiClient(); /** * Login a userA, so that he can see his feeds, conversations, profile, etc. @@ -18,13 +19,14 @@ function useLogin() { * Email and password are required. * @returns true if login was successful, false otherwise */ - async function loginGoogleUser(response: CredentialResponse): Promise { + async function loginGoogleUserA(response: CredentialResponse): Promise { + // const quizId = quizIdA || quizIdB; try { if (!response) { throw new Error('No credential received from Google'); } - const data = await postGoogleLogin(response.toString()); + const data = await apiClient.postGoogleLogin(response.toString(), quizIdA); showSuccessToast(`Welcome back, ${data.user.first_name}!`); const { first_name, last_name, email, quiz_id, user_uuid } = data.user; @@ -44,6 +46,35 @@ function useLogin() { return false; } } + + async function loginGoogleUserB(response: CredentialResponse): Promise { + try { + if (!response) { + throw new Error('No credential received from Google'); + } + + const data = await apiClient.postGoogleLogin(response.toString(), quizIdB); + console.log('data', data); + + showSuccessToast(`Welcome back, ${data.user.first_name}!`); + const { first_name, last_name, email, quiz_id, user_uuid } = data.user; + + dispatch( + loginB({ + firstName: first_name, + lastName: last_name, + email: email, + userId: user_uuid, + quizId: quiz_id, + }) + ); + return true; + } catch (error) { + showErrorToast(error.response?.data.error ?? 'Unexpected Error. Please try again.'); + return false; + } + } + async function loginUserA(email: string, password: string): Promise { try { const data = await apiClient.postLogin(email, password); @@ -93,7 +124,7 @@ function useLogin() { } } - return { loginUserA, loginUserB, loginGoogleUser }; + return { loginUserA, loginUserB, loginGoogleUserA, loginGoogleUserB }; } export default useLogin; diff --git a/src/pages/UserBPages/UserBLoginPage.tsx b/src/pages/UserBPages/UserBLoginPage.tsx index e63d58c2..77d6aa5b 100644 --- a/src/pages/UserBPages/UserBLoginPage.tsx +++ b/src/pages/UserBPages/UserBLoginPage.tsx @@ -1,10 +1,11 @@ import { useState } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; +import { useLocation, useNavigate, useParams } from 'react-router-dom'; 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'; +import GoogleLogin from 'features/auth/components/GoogleLogin'; function UserBLoginPage() { const navigate = useNavigate(); @@ -14,6 +15,7 @@ function UserBLoginPage() { // Logic for login const [isLoading, setIsLoading] = useState(false); const { loginUserB } = useLogin(); + const location = useLocation(); async function handleSubmit(email: string, password: string) { setIsLoading(true); @@ -22,6 +24,14 @@ function UserBLoginPage() { setIsLoading(false); } + function navigateAfterLogin() { + if (location.state && 'from' in location.state) { + navigate(location.state.from); + } else { + navigate(ROUTES.CLIMATE_FEED_PAGE); + } + } + // Logic for password reset const { sendPasswordResetLink } = useResetPassword(); const [showPasswordResetModal, setShowPasswordResetModal] = useState(false); @@ -32,16 +42,17 @@ function UserBLoginPage() { } return ( - {isMobile && navigate(-1)} style={styles.backButton} />} Climate Mind Logo Climate Mind Logo - - setShowPasswordResetModal(true)} /> - +
+ setShowPasswordResetModal(true)} /> +
+ +
setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} />
diff --git a/src/pages/UserBPages/UserBSharedSuccessPage.tsx b/src/pages/UserBPages/UserBSharedSuccessPage.tsx index cf84e564..b5107018 100644 --- a/src/pages/UserBPages/UserBSharedSuccessPage.tsx +++ b/src/pages/UserBPages/UserBSharedSuccessPage.tsx @@ -1,3 +1,4 @@ +import { useSelector } from 'react-redux'; import { useNavigate, useParams } from 'react-router-dom'; import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; import CloudDoneIcon from '@mui/icons-material/CloudDone'; @@ -7,8 +8,10 @@ import { capitalize } from 'helpers/capitalize'; import { CmButton, CmTypography, Page, PageContent } from 'shared/components'; import { FooterAppBar } from 'features/userB/components'; import { useConversation } from 'features/conversations'; +import { RootState } from 'store/store'; function UserBSharedSuccessPage() { + const isUserBLoggedIn = useSelector((state: RootState) => state.auth.userB.isLoggedIn); const navigate = useNavigate(); const { conversationId } = useParams(); @@ -29,20 +32,13 @@ function UserBSharedSuccessPage() { return ( - Shared! + Shared! - {conversation && - {capitalize(conversation.userA.name)} can now see which values, - impacts, and solutions you have in common and will be in touch soon! - } + {conversation && {capitalize(conversation.userA.name)} can now see which values, impacts, and solutions you have in common and will be in touch soon!} - + - + Until then, why not create your own account? @@ -74,12 +70,7 @@ function UserBSharedSuccessPage() { /> )} - handleCreateAccount()} - style={{ margin: 'auto' }} - /> + {!isUserBLoggedIn && handleCreateAccount()} style={{ margin: 'auto' }} />} ); diff --git a/src/pages/UserBPages/UserBSharedSummaryPage.tsx b/src/pages/UserBPages/UserBSharedSummaryPage.tsx index 89f069a5..cdde6aa5 100644 --- a/src/pages/UserBPages/UserBSharedSummaryPage.tsx +++ b/src/pages/UserBPages/UserBSharedSummaryPage.tsx @@ -1,3 +1,4 @@ +import { useSelector } from 'react-redux'; import { useNavigate, useParams } from 'react-router-dom'; import ROUTES_CONFIG from '../../router/RouteConfig'; @@ -10,6 +11,7 @@ import { useAppSelector } from 'store/hooks'; import { RootState } from 'store/store'; function UserBSharedSummaryPage() { + const isUserBLoggedIn = useSelector((state: RootState) => state.auth.userB.isLoggedIn); const navigate = useNavigate(); const { conversationId } = useParams(); @@ -38,30 +40,36 @@ function UserBSharedSummaryPage() { return ( - {conversation && !conversation.consent && (<> - Sharing is caring! - Share the impact and solutions you selected with {capitalizeFirstLetter(conversation.userA.name)} and let them know which core values you share! - )} + {conversation && !conversation.consent && ( + <> + Sharing is caring! + Share the impact and solutions you selected with {capitalizeFirstLetter(conversation.userA.name)} and let them know which core values you share! + + )} - {conversation && conversation.consent && (<> - Share Summary - Here are the topics you shared with {capitalizeFirstLetter(conversation.userA.name)}. - )} + {conversation && conversation.consent && ( + <> + Share Summary + Here are the topics you shared with {capitalizeFirstLetter(conversation.userA.name)}. + + )} {isLoading && } - {alignmentSummary.data && ( - + {alignmentSummary.data && } + + {selectedTopics.data && ( + <> + {selectedTopics.data.climateEffects && } + {selectedTopics.data.climateSolutions && ( + <> + + + + )} + )} - {selectedTopics.data && (<> - {selectedTopics.data.climateEffects && } - {selectedTopics.data.climateSolutions && <> - - - } - )} - {conversation && !conversation.consent && ( We only share your matching core values, selected impact and solutions with {capitalizeFirstLetter(conversation.userA.name)}. No other information, in case you were wondering. :) @@ -73,7 +81,7 @@ function UserBSharedSummaryPage() { {!conversation.consent && handleNotNow()} style={{ backgroundColor: 'transparent', borderColor: 'black' }} />} {!conversation.consent && handleShareWithUserA()} />} - {conversation.consent && handleCreateAccount()} style={{ margin: 'auto' }} />} + {!isUserBLoggedIn && conversation.consent && handleCreateAccount()} style={{ margin: 'auto' }} />} )} diff --git a/src/pages/UserBPages/UserBSignUpPage.tsx b/src/pages/UserBPages/UserBSignUpPage.tsx index 6ccad691..7a88466f 100644 --- a/src/pages/UserBPages/UserBSignUpPage.tsx +++ b/src/pages/UserBPages/UserBSignUpPage.tsx @@ -1,6 +1,6 @@ -import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; import { v4 as uuidv4 } from 'uuid'; +import { useEffect, useState } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; import ROUTES from '../../router/RouteConfig'; import { RegistrationPageOpenEvent, analyticsService } from 'services'; @@ -8,15 +8,18 @@ import { useAppSelector } from 'store/hooks'; import { CmBackButton, CmButton, CmTypography, Page, PageContent } from 'shared/components'; import { SignUpForm, useSignUp } from 'features/auth'; import { useMobileView } from 'shared/hooks'; +import GoogleLogin from 'features/auth/components/GoogleLogin'; function UserBSignUpPage() { const signUpId = uuidv4(); + const navigate = useNavigate(); + const location = useLocation(); const isMobile = useMobileView(); - const [isLoading, setIsLoading] = useState(false); + const { sessionId, quizId } = useAppSelector((state) => state.auth.userB); const { signUp } = useSignUp(); - const { sessionId, quizId } = useAppSelector(state => state.auth.userB); + const [isLoading, setIsLoading] = useState(false); async function signUpHandler(firstName: string, lastName: string, email: string, password: string) { setIsLoading(true); @@ -25,6 +28,13 @@ function UserBSignUpPage() { setIsLoading(false); } + function navigateAfterLogin() { + if (location.state && 'from' in location.state) { + navigate(location.state.from); + } else { + navigate(ROUTES.CLIMATE_FEED_PAGE); + } + } useEffect(() => { if (sessionId) analyticsService.postEvent(RegistrationPageOpenEvent, signUpId); }, [signUpId, sessionId]); @@ -38,10 +48,13 @@ function UserBSignUpPage() {
Already have an account? - navigate(ROUTES.LOGIN_PAGE)} style={styles.loginButton} /> + navigate(ROUTES.LOGIN_PAGE)} style={styles.loginButton} /> +
+
+ +
+
- -
); diff --git a/src/shared/hooks/useApiClient.tsx b/src/shared/hooks/useApiClient.tsx index 42b8db24..fe993d4d 100644 --- a/src/shared/hooks/useApiClient.tsx +++ b/src/shared/hooks/useApiClient.tsx @@ -148,7 +148,7 @@ function useApiClient() { return response.data; } - async function postGoogleLogin(credential: string) { + async function postGoogleLogin(credential: string, quizId: string) { if (quizId) { const response = await apiCall('post', '/auth/google', {}, { credential, quizId }, true); return response.data; From 122398bf24f4f849567aea76189141a03cc86f9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Sep 2024 08:19:00 +0200 Subject: [PATCH 49/61] Bump @mui/material from 5.16.5 to 5.16.7 (#642) Bumps [@mui/material](https://github.com/mui/material-ui/tree/HEAD/packages/mui-material) from 5.16.5 to 5.16.7. - [Release notes](https://github.com/mui/material-ui/releases) - [Changelog](https://github.com/mui/material-ui/blob/v5.16.7/CHANGELOG.md) - [Commits](https://github.com/mui/material-ui/commits/v5.16.7/packages/mui-material) --- updated-dependencies: - dependency-name: "@mui/material" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 77 ++++++++++++++++++----------------------------- package.json | 2 +- 2 files changed, 30 insertions(+), 49 deletions(-) diff --git a/package-lock.json b/package-lock.json index 198a81ac..2b228fd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@mui/icons-material": "^5.16.6", - "@mui/material": "^5.15.18", + "@mui/material": "^5.16.7", "@react-oauth/google": "^0.12.1", "@reduxjs/toolkit": "^2.2.5", "@sentry/cli": "^2.32.1", @@ -2579,7 +2579,6 @@ "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", @@ -2593,7 +2592,6 @@ "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", @@ -2616,7 +2614,6 @@ "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" }, @@ -2628,7 +2625,6 @@ "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" } @@ -2637,7 +2633,6 @@ "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" } @@ -2678,7 +2673,6 @@ "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" }, @@ -4321,9 +4315,9 @@ "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==" }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.5.tgz", - "integrity": "sha512-ziFn1oPm6VjvHQcdGcAO+fXvOQEgieIj0BuSqcltFU+JXIxjPdVYNTdn2HU7/Ak5Gabk6k2u7+9PV7oZ6JT5sA==", + "version": "5.16.7", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.7.tgz", + "integrity": "sha512-RtsCt4Geed2/v74sbihWzzRs+HsIQCfclHeORh5Ynu2fS4icIKozcSubwuG7vtzq2uW3fOR1zITSP84TNt2GoQ==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" @@ -4355,15 +4349,15 @@ } }, "node_modules/@mui/material": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.5.tgz", - "integrity": "sha512-eQrjjg4JeczXvh/+8yvJkxWIiKNHVptB/AqpsKfZBWp5mUD5U3VsjODMuUl1K2BSq0omV3CiO/mQmWSSMKSmaA==", + "version": "5.16.7", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.7.tgz", + "integrity": "sha512-cwwVQxBhK60OIOqZOVLFt55t01zmarKJiJUWbk0+8s/Ix5IaUzAShqlJchxsIQ4mSrWqgcKCCXKtIlG5H+/Jmg==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/core-downloads-tracker": "^5.16.5", - "@mui/system": "^5.16.5", + "@mui/core-downloads-tracker": "^5.16.7", + "@mui/system": "^5.16.7", "@mui/types": "^7.2.15", - "@mui/utils": "^5.16.5", + "@mui/utils": "^5.16.6", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.10", "clsx": "^2.1.0", @@ -4399,12 +4393,12 @@ } }, "node_modules/@mui/private-theming": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.5.tgz", - "integrity": "sha512-CSLg0YkpDqg0aXOxtjo3oTMd3XWMxvNb5d0v4AYVqwOltU8q6GvnZjhWyCLjGSCrcgfwm6/VDjaKLPlR14wxIA==", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.6.tgz", + "integrity": "sha512-rAk+Rh8Clg7Cd7shZhyt2HGTTE5wYKNSJ5sspf28Fqm/PZ69Er9o6KX25g03/FG2dfpg5GCwZh/xOojiTfm3hw==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.16.5", + "@mui/utils": "^5.16.6", "prop-types": "^15.8.1" }, "engines": { @@ -4425,9 +4419,9 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.16.4", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.4.tgz", - "integrity": "sha512-0+mnkf+UiAmTVB8PZFqOhqf729Yh0Cxq29/5cA3VAyDVTRIUUQ8FXQhiAhUIbijFmM72rY80ahFPXIm4WDbzcA==", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.6.tgz", + "integrity": "sha512-zaThmS67ZmtHSWToTiHslbI8jwrmITcN93LQaR2lKArbvS7Z3iLkwRoiikNWutx9MBs8Q6okKvbZq1RQYB3v7g==", "dependencies": { "@babel/runtime": "^7.23.9", "@emotion/cache": "^11.11.0", @@ -4456,15 +4450,15 @@ } }, "node_modules/@mui/system": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.5.tgz", - "integrity": "sha512-uzIUGdrWddUx1HPxW4+B2o4vpgKyRxGe/8BxbfXVDPNPHX75c782TseoCnR/VyfnZJfqX87GcxDmnZEE1c031g==", + "version": "5.16.7", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.7.tgz", + "integrity": "sha512-Jncvs/r/d/itkxh7O7opOunTqbbSSzMTHzZkNLM+FjAOg+cYAZHrPDlYe1ZGKUYORwwb2XexlWnpZp0kZ4AHuA==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.16.5", - "@mui/styled-engine": "^5.16.4", + "@mui/private-theming": "^5.16.6", + "@mui/styled-engine": "^5.16.6", "@mui/types": "^7.2.15", - "@mui/utils": "^5.16.5", + "@mui/utils": "^5.16.6", "clsx": "^2.1.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -4508,9 +4502,9 @@ } }, "node_modules/@mui/utils": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.5.tgz", - "integrity": "sha512-CwhcA9y44XwK7k2joL3Y29mRUnoBt+gOZZdGyw7YihbEwEErJYBtDwbZwVgH68zAljGe/b+Kd5bzfl63Gi3R2A==", + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.6.tgz", + "integrity": "sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==", "dependencies": { "@babel/runtime": "^7.23.9", "@mui/types": "^7.2.15", @@ -9342,7 +9336,6 @@ "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", @@ -9501,7 +9494,6 @@ "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" @@ -9517,7 +9509,6 @@ "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" }, @@ -9529,7 +9520,6 @@ "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" }, @@ -9544,7 +9534,6 @@ "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" @@ -9560,7 +9549,6 @@ "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" }, @@ -9571,14 +9559,12 @@ "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==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "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" } @@ -9587,7 +9573,6 @@ "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" }, @@ -9599,7 +9584,6 @@ "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", @@ -9974,7 +9958,6 @@ "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" }, @@ -10134,7 +10117,6 @@ "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" @@ -11234,7 +11216,7 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", - "dev": true + "devOptional": true }, "node_modules/import-fresh": { "version": "3.3.0", @@ -20954,7 +20936,7 @@ "version": "1.77.8", "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", - "dev": true, + "devOptional": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -22694,7 +22676,6 @@ "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 3885c920..e2cd59e9 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@mui/icons-material": "^5.16.6", - "@mui/material": "^5.15.18", + "@mui/material": "^5.16.7", "@react-oauth/google": "^0.12.1", "@reduxjs/toolkit": "^2.2.5", "@sentry/cli": "^2.32.1", From 8a17b95bb99e32e563cea9a08b2dd7d994ad0118 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Sep 2024 08:19:34 +0200 Subject: [PATCH 50/61] Bump react-router-dom from 6.25.1 to 6.26.1 (#647) Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.25.1 to 6.26.1. - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.26.1/packages/react-router-dom) --- updated-dependencies: - dependency-name: react-router-dom dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 26 +++++++++++++------------- package.json | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b228fd2..615ca201 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,7 @@ "react-markdown": "^9.0.1", "react-query": "^3.39.3", "react-redux": "^9.1.2", - "react-router-dom": "^6.24.1", + "react-router-dom": "^6.26.1", "react-scripts": "^5.0.1", "uuid": "^10.0.0" }, @@ -4696,9 +4696,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.18.0.tgz", - "integrity": "sha512-L3jkqmqoSVBVKHfpGZmLrex0lxR5SucGA0sUfFzGctehw+S/ggL9L/0NnC5mw6P8HUWpFZ3nQw3cRApjjWx9Sw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz", + "integrity": "sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==", "engines": { "node": ">=14.0.0" } @@ -19626,11 +19626,11 @@ } }, "node_modules/react-router": { - "version": "6.25.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.25.1.tgz", - "integrity": "sha512-u8ELFr5Z6g02nUtpPAggP73Jigj1mRePSwhS/2nkTrlPU5yEkH1vYzWNyvSnSzeeE2DNqWdH+P8OhIh9wuXhTw==", + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz", + "integrity": "sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==", "dependencies": { - "@remix-run/router": "1.18.0" + "@remix-run/router": "1.19.1" }, "engines": { "node": ">=14.0.0" @@ -19640,12 +19640,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.25.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.25.1.tgz", - "integrity": "sha512-0tUDpbFvk35iv+N89dWNrJp+afLgd+y4VtorJZuOCXK0kkCWjEvb3vTJM++SYvMEpbVwXKf3FjeVveVEb6JpDQ==", + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.1.tgz", + "integrity": "sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==", "dependencies": { - "@remix-run/router": "1.18.0", - "react-router": "6.25.1" + "@remix-run/router": "1.19.1", + "react-router": "6.26.1" }, "engines": { "node": ">=14.0.0" diff --git a/package.json b/package.json index e2cd59e9..4126cc20 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "react-markdown": "^9.0.1", "react-query": "^3.39.3", "react-redux": "^9.1.2", - "react-router-dom": "^6.24.1", + "react-router-dom": "^6.26.1", "react-scripts": "^5.0.1", "uuid": "^10.0.0" }, From 894b798167414b5baebf9a3095376dc1a1983294 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Sep 2024 08:20:02 +0200 Subject: [PATCH 51/61] Bump @sentry/react from 7.118.0 to 8.28.0 (#666) Bumps [@sentry/react](https://github.com/getsentry/sentry-javascript) from 7.118.0 to 8.28.0. - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-javascript/compare/7.118.0...8.28.0) --- updated-dependencies: - dependency-name: "@sentry/react" dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 186 +++++++++++++++++++--------------------------- package.json | 2 +- 2 files changed, 76 insertions(+), 112 deletions(-) diff --git a/package-lock.json b/package-lock.json index 615ca201..3a1373ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@react-oauth/google": "^0.12.1", "@reduxjs/toolkit": "^2.2.5", "@sentry/cli": "^2.32.1", - "@sentry/react": "^7.114.0", + "@sentry/react": "^8.28.0", "@sentry/tracing": "^7.114.0", "@tanstack/react-query": "^5.51.21", "axios": "^1.7.3", @@ -4782,62 +4782,75 @@ "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==" }, + "node_modules/@sentry-internal/browser-utils": { + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.28.0.tgz", + "integrity": "sha512-tE9++KEy8SlqibTmYymuxFVAnutsXBqrwQ936WJbjaMfkqXiro7C1El0ybkprskd0rKS7kln20Q6nQlNlMEoTA==", + "dependencies": { + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" + }, + "engines": { + "node": ">=14.18" + } + }, "node_modules/@sentry-internal/feedback": { - "version": "7.118.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.118.0.tgz", - "integrity": "sha512-IYOGRcqIqKJJpMwBBv+0JTu0FPpXnakJYvOx/XEa/SNyF5+l7b9gGEjUVWh1ok50kTLW/XPnpnXNAGQcoKHg+w==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.28.0.tgz", + "integrity": "sha512-5vYunPCDBLCJ8QNnhepacdYheiN+UtYxpGAIaC/zjBC1nDuBgWs+TfKPo1UlO/1sesfgs9ibpxtShOweucL61g==", "dependencies": { - "@sentry/core": "7.118.0", - "@sentry/types": "7.118.0", - "@sentry/utils": "7.118.0" + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "engines": { - "node": ">=12" + "node": ">=14.18" } }, - "node_modules/@sentry-internal/replay-canvas": { - "version": "7.118.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-7.118.0.tgz", - "integrity": "sha512-XxHlCClvrxmVKpiZetFYyiBaPQNiojoBGFFVgbbWBIAPc+fWeLJ2BMoQEBjn/0NA/8u8T6lErK5YQo/eIx9+XQ==", + "node_modules/@sentry-internal/replay": { + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.28.0.tgz", + "integrity": "sha512-70jvzzOL5O74gahgXKyRkZgiYN93yly5gq+bbj4/6NRQ+EtPd285+ccy0laExdfyK0ugvvwD4v+1MQit52OAsg==", "dependencies": { - "@sentry/core": "7.118.0", - "@sentry/replay": "7.118.0", - "@sentry/types": "7.118.0", - "@sentry/utils": "7.118.0" + "@sentry-internal/browser-utils": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "engines": { - "node": ">=12" + "node": ">=14.18" } }, - "node_modules/@sentry-internal/tracing": { - "version": "7.118.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.118.0.tgz", - "integrity": "sha512-dERAshKlQLrBscHSarhHyUeGsu652bDTUN1FK0m4e3X48M3I5/s+0N880Qjpe5MprNLcINlaIgdQ9jkisvxjfw==", + "node_modules/@sentry-internal/replay-canvas": { + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.28.0.tgz", + "integrity": "sha512-RfpYHDHMUKGeEdx41QtHITjEn6P3tGaDPHvatqdrD3yv4j+wbJ6laX1PrIxCpGFUtjdzkqi/KUcvUd2kzbH/FA==", "dependencies": { - "@sentry/core": "7.118.0", - "@sentry/types": "7.118.0", - "@sentry/utils": "7.118.0" + "@sentry-internal/replay": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "engines": { - "node": ">=8" + "node": ">=14.18" } }, "node_modules/@sentry/browser": { - "version": "7.118.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.118.0.tgz", - "integrity": "sha512-8onDOFV1VLEoBuqA5yaJeR3FF1JNuxr5C7p1oN3OwY724iTVqQnOLmZKZaSnHV3RkY67wKDGQkQIie14sc+42g==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.28.0.tgz", + "integrity": "sha512-i/gjMYzIGQiPFH1pCbdnTwH9xs9mTAqzN+goP3GWX5a58frc7h8vxyA/5z0yMd0aCW6U8mVxnoAT72vGbKbx0g==", "dependencies": { - "@sentry-internal/feedback": "7.118.0", - "@sentry-internal/replay-canvas": "7.118.0", - "@sentry-internal/tracing": "7.118.0", - "@sentry/core": "7.118.0", - "@sentry/integrations": "7.118.0", - "@sentry/replay": "7.118.0", - "@sentry/types": "7.118.0", - "@sentry/utils": "7.118.0" + "@sentry-internal/browser-utils": "8.28.0", + "@sentry-internal/feedback": "8.28.0", + "@sentry-internal/replay": "8.28.0", + "@sentry-internal/replay-canvas": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "engines": { - "node": ">=8" + "node": ">=14.18" } }, "node_modules/@sentry/cli": { @@ -4884,61 +4897,33 @@ } }, "node_modules/@sentry/core": { - "version": "7.118.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.118.0.tgz", - "integrity": "sha512-ol0xBdp3/K11IMAYSQE0FMxBOOH9hMsb/rjxXWe0hfM5c72CqYWL3ol7voPci0GELJ5CZG+9ImEU1V9r6gK64g==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.28.0.tgz", + "integrity": "sha512-+If9uubvpZpvaQQw4HLiKPhrSS9/KcoA/AcdQkNm+5CVwAoOmDPtyYfkPBgfo2hLZnZQqR1bwkz/PrNoOm+gqA==", "dependencies": { - "@sentry/types": "7.118.0", - "@sentry/utils": "7.118.0" + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/integrations": { - "version": "7.118.0", - "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.118.0.tgz", - "integrity": "sha512-C2rR4NvIMjokF8jP5qzSf1o2zxDx7IeYnr8u15Kb2+HdZtX559owALR0hfgwnfeElqMhGlJBaKUWZ48lXJMzCQ==", - "dependencies": { - "@sentry/core": "7.118.0", - "@sentry/types": "7.118.0", - "@sentry/utils": "7.118.0", - "localforage": "^1.8.1" - }, - "engines": { - "node": ">=8" + "node": ">=14.18" } }, "node_modules/@sentry/react": { - "version": "7.118.0", - "resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.118.0.tgz", - "integrity": "sha512-oEYe5TGk8S7YzPsFqDf4xDHjfzs35/QFE+dou3S2d24OYpso8Tq4C5f1VzYmnOOyy85T7JNicYLSo0n0NSJvQg==", - "dependencies": { - "@sentry/browser": "7.118.0", - "@sentry/core": "7.118.0", - "@sentry/types": "7.118.0", - "@sentry/utils": "7.118.0", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-8.28.0.tgz", + "integrity": "sha512-rpeO8ikpAK7/9kVHc1IMtJc7A7IyPzswcFJ0uL1faCt8oZEzlotrQnEe6hgFnv4xvMledTrohnKj/fWVd55Aig==", + "dependencies": { + "@sentry/browser": "8.28.0", + "@sentry/core": "8.28.0", + "@sentry/types": "8.28.0", + "@sentry/utils": "8.28.0", "hoist-non-react-statics": "^3.3.2" }, "engines": { - "node": ">=8" + "node": ">=14.18" }, "peerDependencies": { - "react": "15.x || 16.x || 17.x || 18.x" - } - }, - "node_modules/@sentry/replay": { - "version": "7.118.0", - "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.118.0.tgz", - "integrity": "sha512-boQfCL+1L/tSZ9Huwi00+VtU+Ih1Lcg8HtxBuAsBCJR9pQgUL5jp7ECYdTeeHyCh/RJO7JqV1CEoGTgohe10mA==", - "dependencies": { - "@sentry-internal/tracing": "7.118.0", - "@sentry/core": "7.118.0", - "@sentry/types": "7.118.0", - "@sentry/utils": "7.118.0" - }, - "engines": { - "node": ">=12" + "react": "^16.14.0 || 17.x || 18.x || 19.x" } }, "node_modules/@sentry/tracing": { @@ -4997,22 +4982,22 @@ } }, "node_modules/@sentry/types": { - "version": "7.118.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.118.0.tgz", - "integrity": "sha512-2drqrD2+6kgeg+W/ycmiti3G4lJrV3hGjY9PpJ3bJeXrh6T2+LxKPzlgSEnKFaeQWkXdZ4eaUbtTXVebMjb5JA==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.28.0.tgz", + "integrity": "sha512-hOfqfd92/AzBrEdMgmmV1VfOXJbIfleFTnerRl0mg/+CcNgP/6+Fdonp354TD56ouWNF2WkOM6sEKSXMWp6SEQ==", "engines": { - "node": ">=8" + "node": ">=14.18" } }, "node_modules/@sentry/utils": { - "version": "7.118.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.118.0.tgz", - "integrity": "sha512-43qItc/ydxZV1Zb3Kn2M54RwL9XXFa3IAYBO8S82Qvq5YUYmU2AmJ1jgg7DabXlVSWgMA1HntwqnOV3JLaEnTQ==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.28.0.tgz", + "integrity": "sha512-smhk7PJpvDMQ2DB5p2qn9UeoUHdU41IgjMmS2xklZpa8tjzBTxDeWpGvrX2fuH67D9bAJuLC/XyZjJCHLoEW5g==", "dependencies": { - "@sentry/types": "7.118.0" + "@sentry/types": "8.28.0" }, "engines": { - "node": ">=8" + "node": ">=14.18" } }, "node_modules/@sinclair/typebox": { @@ -11198,11 +11183,6 @@ "node": ">= 4" } }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" - }, "node_modules/immer": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", @@ -15965,14 +15945,6 @@ "node": ">= 0.8.0" } }, - "node_modules/lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", - "dependencies": { - "immediate": "~3.0.5" - } - }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -16034,14 +16006,6 @@ "node": ">=8.9.0" } }, - "node_modules/localforage": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", - "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", - "dependencies": { - "lie": "3.1.1" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", diff --git a/package.json b/package.json index 4126cc20..f111f336 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@react-oauth/google": "^0.12.1", "@reduxjs/toolkit": "^2.2.5", "@sentry/cli": "^2.32.1", - "@sentry/react": "^7.114.0", + "@sentry/react": "^8.28.0", "@sentry/tracing": "^7.114.0", "@tanstack/react-query": "^5.51.21", "axios": "^1.7.3", From 095968750ce0ad89ea311bf4fba4f2e633b1a556 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Sep 2024 08:23:01 +0200 Subject: [PATCH 52/61] Bump @sentry/cli from 2.33.0 to 2.35.0 (#669) Bumps [@sentry/cli](https://github.com/getsentry/sentry-cli) from 2.33.0 to 2.35.0. - [Release notes](https://github.com/getsentry/sentry-cli/releases) - [Changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-cli/compare/2.33.0...2.35.0) --- updated-dependencies: - dependency-name: "@sentry/cli" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 121 ++++++++++++++++++++++++++++++++++++++++------ package.json | 2 +- 2 files changed, 108 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3a1373ff..a32bb195 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@mui/material": "^5.16.7", "@react-oauth/google": "^0.12.1", "@reduxjs/toolkit": "^2.2.5", - "@sentry/cli": "^2.32.1", + "@sentry/cli": "^2.35.0", "@sentry/react": "^8.28.0", "@sentry/tracing": "^7.114.0", "@tanstack/react-query": "^5.51.21", @@ -4854,9 +4854,9 @@ } }, "node_modules/@sentry/cli": { - "version": "2.33.0", - "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.33.0.tgz", - "integrity": "sha512-9MOzQy1UunVBhPOfEuO0JH2ofWAMmZVavTTR/Bo2CkJwI1qjyVF0UKLTXE3l4ujiJnFufOoBsVyKmYWXFerbCw==", + "version": "2.35.0", + "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.35.0.tgz", + "integrity": "sha512-7sHRJViEgHTfEXf+HD1Fb2cwmnxlILmb2NNxghP2vvrgC2PhuwuJU7AX4zg7HjJgxH9HBmnn4AJskDujaJ/6cQ==", "hasInstallScript": true, "dependencies": { "https-proxy-agent": "^5.0.0", @@ -4872,19 +4872,112 @@ "node": ">= 10" }, "optionalDependencies": { - "@sentry/cli-darwin": "2.33.0", - "@sentry/cli-linux-arm": "2.33.0", - "@sentry/cli-linux-arm64": "2.33.0", - "@sentry/cli-linux-i686": "2.33.0", - "@sentry/cli-linux-x64": "2.33.0", - "@sentry/cli-win32-i686": "2.33.0", - "@sentry/cli-win32-x64": "2.33.0" + "@sentry/cli-darwin": "2.35.0", + "@sentry/cli-linux-arm": "2.35.0", + "@sentry/cli-linux-arm64": "2.35.0", + "@sentry/cli-linux-i686": "2.35.0", + "@sentry/cli-linux-x64": "2.35.0", + "@sentry/cli-win32-i686": "2.35.0", + "@sentry/cli-win32-x64": "2.35.0" + } + }, + "node_modules/@sentry/cli-darwin": { + "version": "2.35.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.35.0.tgz", + "integrity": "sha512-dRtDaASkB1ncSbCLMIL8bxki4dPMimSdYz74XOUJ5IvDVVzEInEO7PqvyOj/cyafB+1FSNudaZ90ZRvsNN1Maw==", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-linux-arm": { + "version": "2.35.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.35.0.tgz", + "integrity": "sha512-zNL+/HnepZ4/MkIS8wfoUQxSa+k6r0DSSdX1TpDH5436u+3LB5rfCTBfZ624DWHKMoXX+1dI+rWSi+zL8QFMsg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux", + "freebsd" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-linux-arm64": { + "version": "2.35.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.35.0.tgz", + "integrity": "sha512-NpyVz2lQWWkMa9GZkt0m4cA/wsgYnWOE6Z+4ePUGjbOIG3Ws9DLaHjYxUUYI79kxfbVCp7wLo1S6kOkj+M1Dlw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux", + "freebsd" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-linux-i686": { + "version": "2.35.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.35.0.tgz", + "integrity": "sha512-vIYwZVqx+kYZdPsenIm+UqjSCKe9Q2Aof6kzrzW0DPR1WyqIWbWG4NbiugiPTiuA1dLjUjYpGP8wyIqb8hxv4w==", + "cpu": [ + "x86", + "ia32" + ], + "optional": true, + "os": [ + "linux", + "freebsd" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-linux-x64": { + "version": "2.35.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.35.0.tgz", + "integrity": "sha512-7Wy5QNt6wZ8EaxEbHqP0DEiyUcXRVItRt9jzhpa2nCaawL+fwDOQCjUkHGsdIC+y14UqA+er9CaPCSp8sA6Vaw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux", + "freebsd" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/cli-win32-i686": { + "version": "2.35.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.35.0.tgz", + "integrity": "sha512-XDcBUtO5A9elH+xgFNs6NBjkMBnz0sZLo5DU7LE77qKXULnlLeJ63eZD1ukQIRPvxEDsIEPOllRweLuAlUMDtw==", + "cpu": [ + "x86", + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" } }, "node_modules/@sentry/cli-win32-x64": { - "version": "2.33.0", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.33.0.tgz", - "integrity": "sha512-GIUKysZ1xbSklY9h1aVaLMSYLsnMSd+JuwQLR+0wKw2wJC4O5kNCPFSGikhiOZM/kvh3GO1WnXNyazFp8nLAzw==", + "version": "2.35.0", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.35.0.tgz", + "integrity": "sha512-86yHO+31qAXUeAdSCH7MNodn/cn/9xd2fTrxjtfNZWO0pX0jW91sCdomfBxhu5b977cyV9gNcqeBbc9XSIKIIA==", "cpu": [ "x64" ], diff --git a/package.json b/package.json index f111f336..b65e6720 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@mui/material": "^5.16.7", "@react-oauth/google": "^0.12.1", "@reduxjs/toolkit": "^2.2.5", - "@sentry/cli": "^2.32.1", + "@sentry/cli": "^2.35.0", "@sentry/react": "^8.28.0", "@sentry/tracing": "^7.114.0", "@tanstack/react-query": "^5.51.21", From 573e3421032508a4ef1cdf1f06e27c9f642532d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 8 Sep 2024 08:25:59 +0200 Subject: [PATCH 53/61] Bump @tanstack/react-query from 5.51.21 to 5.55.0 (#670) Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.51.21 to 5.55.0. - [Release notes](https://github.com/TanStack/query/releases) - [Commits](https://github.com/TanStack/query/commits/v5.55.0/packages/react-query) --- updated-dependencies: - dependency-name: "@tanstack/react-query" dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 18 +++++++++--------- package.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index a32bb195..1e3455e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@sentry/cli": "^2.35.0", "@sentry/react": "^8.28.0", "@sentry/tracing": "^7.114.0", - "@tanstack/react-query": "^5.51.21", + "@tanstack/react-query": "^5.55.0", "axios": "^1.7.3", "chart.js": "^4.4.3", "gapi-script": "^1.2.0", @@ -5334,27 +5334,27 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.51.21", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.51.21.tgz", - "integrity": "sha512-POQxm42IUp6n89kKWF4IZi18v3fxQWFRolvBA6phNVmA8psdfB1MvDnGacCJdS+EOX12w/CyHM62z//rHmYmvw==", + "version": "5.54.1", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.54.1.tgz", + "integrity": "sha512-hKS+WRpT5zBFip21pB6Jx1C0hranWQrbv5EJ7qPoiV5MYI3C8rTCqWC9DdBseiPT1JgQWh8Y55YthuYZNiw3Xw==", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" } }, "node_modules/@tanstack/react-query": { - "version": "5.51.21", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.51.21.tgz", - "integrity": "sha512-Q/V81x3sAYgCsxjwOkfLXfrmoG+FmDhLeHH5okC/Bp8Aaw2c33lbEo/mMcMnkxUPVtB2FLpzHT0tq3c+OlZEbw==", + "version": "5.55.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.55.0.tgz", + "integrity": "sha512-2uYuxEbRQD8TORUiTUacEOwt1e8aoSqUOJFGY5TUrh6rQ3U85zrMS2wvbNhBhXGh6Vj69QDCP2yv8tIY7joo6Q==", "dependencies": { - "@tanstack/query-core": "5.51.21" + "@tanstack/query-core": "5.54.1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "react": "^18.0.0" + "react": "^18 || ^19" } }, "node_modules/@tootallnate/once": { diff --git a/package.json b/package.json index b65e6720..cd0e01ac 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@sentry/cli": "^2.35.0", "@sentry/react": "^8.28.0", "@sentry/tracing": "^7.114.0", - "@tanstack/react-query": "^5.51.21", + "@tanstack/react-query": "^5.55.0", "axios": "^1.7.3", "chart.js": "^4.4.3", "gapi-script": "^1.2.0", From b17e17ae0c3113a921b3570e5ee62768dbb3e73b Mon Sep 17 00:00:00 2001 From: TrishaChetani Date: Sun, 8 Sep 2024 09:02:53 +0200 Subject: [PATCH 54/61] fix: Dockerfile to reduce vulnerabilities (#674) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-ALPINE311-FREETYPE-1019647 - https://snyk.io/vuln/SNYK-ALPINE311-APKTOOLS-1534687 - https://snyk.io/vuln/SNYK-ALPINE311-OPENSSL-1569451 - https://snyk.io/vuln/SNYK-ALPINE311-OPENSSL-1569451 - https://snyk.io/vuln/SNYK-ALPINE311-ZLIB-2977081 Co-authored-by: snyk-bot --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 239412f4..6ae76178 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ COPY . ./ RUN npm run build ##stage 2. build the production (server) environment -FROM nginx:1.17-alpine +FROM nginx:1.23.2-alpine #install curl and envsubst RUN apk --no-cache add curl From d28dd0618e2723cdc18ba2b9f59679d22b907421 Mon Sep 17 00:00:00 2001 From: TrishaChetani Date: Sun, 8 Sep 2024 09:03:06 +0200 Subject: [PATCH 55/61] fix: Dockerfile.local to reduce vulnerabilities (#673) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-ALPINE311-FREETYPE-1019647 - https://snyk.io/vuln/SNYK-ALPINE311-APKTOOLS-1534687 - https://snyk.io/vuln/SNYK-ALPINE311-OPENSSL-1569451 - https://snyk.io/vuln/SNYK-ALPINE311-OPENSSL-1569451 - https://snyk.io/vuln/SNYK-ALPINE311-ZLIB-2977081 Co-authored-by: snyk-bot --- Dockerfile.local | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.local b/Dockerfile.local index 239412f4..6ae76178 100644 --- a/Dockerfile.local +++ b/Dockerfile.local @@ -11,7 +11,7 @@ COPY . ./ RUN npm run build ##stage 2. build the production (server) environment -FROM nginx:1.17-alpine +FROM nginx:1.23.2-alpine #install curl and envsubst RUN apk --no-cache add curl From e4fc3b94a014855c177ab703836c6aceb41da8ff Mon Sep 17 00:00:00 2001 From: TrishaChetani Date: Sun, 8 Sep 2024 09:03:17 +0200 Subject: [PATCH 56/61] fix: Dockerfile.production to reduce vulnerabilities (#672) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-ALPINE311-FREETYPE-1019647 - https://snyk.io/vuln/SNYK-ALPINE311-APKTOOLS-1534687 - https://snyk.io/vuln/SNYK-ALPINE311-OPENSSL-1569451 - https://snyk.io/vuln/SNYK-ALPINE311-OPENSSL-1569451 - https://snyk.io/vuln/SNYK-ALPINE311-ZLIB-2977081 Co-authored-by: snyk-bot --- Dockerfile.production | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.production b/Dockerfile.production index 4702ca86..cf5fa0e9 100644 --- a/Dockerfile.production +++ b/Dockerfile.production @@ -11,7 +11,7 @@ COPY . ./ RUN npm run build ##stage 2. build the production (server) environment -FROM nginx:1.17-alpine +FROM nginx:1.23.2-alpine # Install curl and envsubst RUN apk --no-cache add curl From 4cabc65f720c3e3123f34243afd17105719af669 Mon Sep 17 00:00:00 2001 From: TrishaChetani Date: Sun, 8 Sep 2024 09:03:29 +0200 Subject: [PATCH 57/61] fix: Dockerfile.test to reduce vulnerabilities (#671) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-ALPINE311-FREETYPE-1019647 - https://snyk.io/vuln/SNYK-ALPINE311-APKTOOLS-1534687 - https://snyk.io/vuln/SNYK-ALPINE311-OPENSSL-1569451 - https://snyk.io/vuln/SNYK-ALPINE311-OPENSSL-1569451 - https://snyk.io/vuln/SNYK-ALPINE311-ZLIB-2977081 Co-authored-by: snyk-bot --- Dockerfile.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.test b/Dockerfile.test index 4e7c59d8..6ab74a83 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -12,7 +12,7 @@ COPY .env.development .env.production RUN npm run build ## Stage 2. build the production (server) environment -FROM nginx:1.17-alpine +FROM nginx:1.23.2-alpine # Install curl and envsubst RUN apk --no-cache add curl From 97d6ac8cd186a6a71fe4f3990fb96beba3872af9 Mon Sep 17 00:00:00 2001 From: Svenstar74 Date: Sun, 8 Sep 2024 10:32:14 +0200 Subject: [PATCH 58/61] small adjustments to quiz and login page --- src/features/auth/components/LoginForm.tsx | 4 ++-- src/features/quiz/components/QuizProgress.tsx | 5 +---- src/pages/UserAUnauthorizedPages/LoginPage.tsx | 3 ++- src/pages/UserBPages/UserBLoginPage.tsx | 6 ++++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/features/auth/components/LoginForm.tsx b/src/features/auth/components/LoginForm.tsx index bd47635e..8399da89 100644 --- a/src/features/auth/components/LoginForm.tsx +++ b/src/features/auth/components/LoginForm.tsx @@ -26,7 +26,6 @@ function LoginForm({ isLoading, onLogin, onForgotPasswordClick }: Props) {
Forgot your password? -
@@ -42,7 +41,8 @@ const styles: { [key: string]: React.CSSProperties } = { display: 'flex', flexDirection: 'column', alignItems: 'center', - width: '100%', + width: '80vw', + maxWidth: 320, }, textInput: { marginTop: 20, diff --git a/src/features/quiz/components/QuizProgress.tsx b/src/features/quiz/components/QuizProgress.tsx index 63deb1ff..1aae3d9a 100644 --- a/src/features/quiz/components/QuizProgress.tsx +++ b/src/features/quiz/components/QuizProgress.tsx @@ -1,4 +1,3 @@ -import { useMediaQuery } from '@mui/material'; import { CmBackButton, CmTypography } from 'shared/components'; interface Props { @@ -9,11 +8,9 @@ interface Props { } function QuizProgress({ onBack, currentQuestionIndex, maxQuestionIndex, alternativeText }: Props) { - const isSmall = useMediaQuery('(max-width: 960px)'); - // We only show the back button on the top for small screens. For larger screens it is beneath the question. // And of course we don't show it on the first question, because there's no question to go back to. - const showBackButtonTop = isSmall && (maxQuestionIndex - currentQuestionIndex) !== 9; + const showBackButtonTop = (maxQuestionIndex - currentQuestionIndex) !== 9; // Calculate the current value of the progress bar. let progress = currentQuestionIndex / 10 - 0.1; diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 79f234e1..d85fc890 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -9,6 +9,7 @@ import { useMobileView } from 'shared/hooks'; function LoginPage() { const devMode = localStorage.getItem('devMode') === 'true'; + const navigate = useNavigate(); const location = useLocation(); const isMobile = useMobileView(); @@ -50,7 +51,7 @@ function LoginPage() {
setShowPasswordResetModal(true)} /> -
+ {devMode &&
} {devMode && }
diff --git a/src/pages/UserBPages/UserBLoginPage.tsx b/src/pages/UserBPages/UserBLoginPage.tsx index 77d6aa5b..143b9759 100644 --- a/src/pages/UserBPages/UserBLoginPage.tsx +++ b/src/pages/UserBPages/UserBLoginPage.tsx @@ -8,6 +8,8 @@ import { useMobileView } from 'shared/hooks'; import GoogleLogin from 'features/auth/components/GoogleLogin'; function UserBLoginPage() { + const devMode = localStorage.getItem('devMode') === 'true'; + const navigate = useNavigate(); const { conversationId } = useParams(); const isMobile = useMobileView(); @@ -50,8 +52,8 @@ function UserBLoginPage() { Climate Mind Logo
setShowPasswordResetModal(true)} /> -
- + {devMode &&
} + {devMode && }
setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> From a24c2edc76066d161ddce67032a9564c70b2ff98 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Wed, 25 Sep 2024 09:16:52 +0100 Subject: [PATCH 59/61] [686] - create code for refreshing google auth token (#690) * feat: Add refresh_token to googleLogin response - Added the `refresh_token` property to the `googleLogin` response type in `responses.ts`. - This change allows the backend to include a refresh token when a user logs in with Google. Refactor: Remove unused code in useLogin.tsx - Removed an extra whitespace in the `useLogin` hook in `useLogin.tsx`. - This change improves code readability and removes unnecessary code. Refactor: Comment out unused code in useApiClient.tsx - Commented out the unused `useNavigate` import and `ROUTES` import in `useApiClient.tsx`. - This change removes unused code and improves code cleanliness. Refactor: Remove unused parameters in apiCall function - Removed the `customCookies` parameter from the `apiCall` function in `useApiClient.tsx`. - This change removes unused code and simplifies the function signature. Fix: Update getAllConversations request headers - Updated the request headers in the `getAllConversations` function in `useApiClient.tsx` to include the access token. - This change ensures that the request is authenticated and authorized. Fix: Handle token refresh in useApiClient.tsx - Added a new `handleTokenRefresh` function in `useApiClient.tsx` to handle token refresh logic. - This change allows the frontend to refresh the access token when it expires. Fix: Update postGoogleLogin to store refresh token - Updated the `postGoogleLogin` function in `useApiClient.tsx` to store the refresh token in cookies. - This change ensures that the refresh token is available for token refresh requests. Fix: Remove unused code in postRegister function - Removed an extra line of code in the `postRegister` function in `useApiClient.tsx`. - This change removes unnecessary code and improves code readability. * feat: Add authorization header to createConversationInvite and deleteConversation API calls * refactor: Remove unused code and console logs in useApiClient hook * refactor: Remove unused code and console logs in useApiClient hook * refactor: Remove unused code and console logs in useApiClient hook * cleanup --------- Co-authored-by: Svenstar74 --- src/api/responses.ts | 1 + src/features/auth/hooks/useLogin.tsx | 2 +- src/pages/UserBPages/UserBSignUpPage.tsx | 3 +- src/shared/hooks/useApiClient.tsx | 121 +++++++++++++++-------- 4 files changed, 85 insertions(+), 42 deletions(-) diff --git a/src/api/responses.ts b/src/api/responses.ts index 015fe4b3..b8e42c69 100644 --- a/src/api/responses.ts +++ b/src/api/responses.ts @@ -55,6 +55,7 @@ export type Login = { export type googleLogin = { message: string; access_token: string; + refresh_token: string; user: { email: string; first_name: string; diff --git a/src/features/auth/hooks/useLogin.tsx b/src/features/auth/hooks/useLogin.tsx index ca254ee8..6f9c56f7 100644 --- a/src/features/auth/hooks/useLogin.tsx +++ b/src/features/auth/hooks/useLogin.tsx @@ -8,7 +8,7 @@ function useLogin() { const dispatch = useAppDispatch(); const quizIdB = useAppSelector((state) => state.auth.userB.quizId); const quizIdA = useAppSelector((state) => state.auth.userA.quizId); - + const apiClient = useApiClient(); const { showSuccessToast, showErrorToast } = useToastMessage(); diff --git a/src/pages/UserBPages/UserBSignUpPage.tsx b/src/pages/UserBPages/UserBSignUpPage.tsx index 7a88466f..d5979529 100644 --- a/src/pages/UserBPages/UserBSignUpPage.tsx +++ b/src/pages/UserBPages/UserBSignUpPage.tsx @@ -20,6 +20,7 @@ function UserBSignUpPage() { const { sessionId, quizId } = useAppSelector((state) => state.auth.userB); const { signUp } = useSignUp(); const [isLoading, setIsLoading] = useState(false); + const devMode = localStorage.getItem('devMode') === 'true'; async function signUpHandler(firstName: string, lastName: string, email: string, password: string) { setIsLoading(true); @@ -53,7 +54,7 @@ function UserBSignUpPage() {
- + {devMode && }
diff --git a/src/shared/hooks/useApiClient.tsx b/src/shared/hooks/useApiClient.tsx index fe993d4d..839ba0aa 100644 --- a/src/shared/hooks/useApiClient.tsx +++ b/src/shared/hooks/useApiClient.tsx @@ -3,7 +3,6 @@ import Cookies from 'js-cookie'; import { jwtDecode } from 'jwt-decode'; import { useAppSelector } from 'store/hooks'; -// import { useLogout } from 'features/auth'; import { useToastMessage } from 'shared/hooks'; import * as requests from 'api/requests'; import * as responses from 'api/responses'; @@ -26,24 +25,42 @@ const validateToken = (token: string): boolean => { function useApiClient() { const { showErrorToast } = useToastMessage(); - const sessionId = useAppSelector((state) => state.auth.userA.sessionId); const quizId = useAppSelector((state) => state.auth.userA.quizId); - async function apiCall(method: string, endpoint: string, headers: { [key: string]: string }, data?: any, withCredentials?: boolean) { + async function apiCall(method: string, endpoint: string, headers: { [key: string]: string }, data?: any, withCredentials?: boolean, customCookies?: { [key: string]: string }) { // Add sessionId to headers if (sessionId) { headers['X-Session-Id'] = sessionId; } + // customCookies is used to set cookies (In this instance it is the refresh token) for the request and remove them after the request is done. + if (customCookies) { + Object.entries(customCookies).forEach(([key, value]) => { + Cookies.set(key, value, { secure: true, sameSite: 'strict' }); + }); + } + // Get access token from cookies - const accessToken = Cookies.get('accessToken'); + let accessToken = Cookies.get('accessToken'); if (accessToken) { + // Check if the token is valid if (!validateToken(accessToken)) { - Cookies.remove('accessToken'); - // const newAccessToken = await postRefresh(); - // headers['Authorization'] = 'Bearer ' + newAccessToken; - } else { + // Attempt to refresh the access token + const newAccessToken = await handleTokenRefresh(); + + // If a new token is received, update it in the cookies and set the Authorization header + if (newAccessToken) { + accessToken = newAccessToken; + Cookies.set('accessToken', accessToken); + } else { + showErrorToast('Your session has expired. Please login again.'); + setTimeout(async () => { + window.location.reload(); + }, 2000); // Add a delay to allow the toast to show before redirecting + } + + // Set the Authorization header with the valid (or refreshed) token headers['Authorization'] = 'Bearer ' + accessToken; } } @@ -53,16 +70,25 @@ function useApiClient() { method, headers, data, - withCredentials, + withCredentials, // Always send credentials unless explicitly set to false }); + if (customCookies) { + Object.keys(customCookies).forEach((key) => { + Cookies.remove(key); + }); + } + return response; } async function postSession() { try { const response = await apiCall('post', '/session', {}); - return response.data; + if (response) { + return response.data; + } + throw new Error('Response is undefined'); } catch (error) { console.log(error); return { sessionId: '' }; @@ -108,9 +134,18 @@ function useApiClient() { async function postRegister({ firstName, lastName, email, password, quizId }: requests.PostRegister) { const response = await apiCall('post', '/register', {}, { firstName, lastName, email, password, quizId }); + const accessToken = response.data.access_token; // Store the access token for userA in cookies - const accessToken = response.data.access_token; + if (response) { + if (!response) { + throw new Error('Response is undefined'); + } + + Cookies.set('accessToken', accessToken, { secure: true }); + } else { + throw new Error('Response is undefined'); + } Cookies.set('accessToken', accessToken, { secure: true }); return response.data; @@ -137,26 +172,25 @@ function useApiClient() { Cookies.set('accessToken', accessToken, { secure: true }); } - // Store the refresh token in cookies - // const cookieHeader = response.headers['set-cookie']; - - // if (cookieHeader) { - // const refreshToken = cookieHeader[0].split(';')[0].split('=')[1]; - // Cookies.set('refreshToken', refreshToken, { expires: 365, secure: true }); - // } - + // Set refresh token from response headers (if backend returns it) + const cookieHeader = response.headers['set-cookie']; + if (cookieHeader) { + const refreshToken = cookieHeader[0].split(';')[0].split('=')[1]; + Cookies.set('refreshToken', refreshToken, { expires: 365, secure: true }); + } return response.data; } async function postGoogleLogin(credential: string, quizId: string) { - if (quizId) { - const response = await apiCall('post', '/auth/google', {}, { credential, quizId }, true); - return response.data; - } + const response = await apiCall('post', '/auth/google', {}, { credential, quizId }, true); + const { access_token, refresh_token } = response.data; + Cookies.set('accessToken', access_token, { secure: true }); + Cookies.set('refreshToken', refresh_token, { + secure: true, + sameSite: 'strict', + path: '/', + }); - const response = await apiCall('post', '/auth/google', {}, { credential }, true); - const { access_token } = response.data; - Cookies.set('accessToken', access_token, { secure: true, sameSite: 'strict' }); return response.data; } @@ -167,30 +201,37 @@ function useApiClient() { await apiCall('post', '/logout', {}); } + async function handleTokenRefresh(): Promise { + const newToken = await postRefresh(); + + if (newToken) { + Cookies.set('accessToken', newToken, { secure: true }); + return newToken; + } else { + showErrorToast('Your session has expired. Please login again.'); + setTimeout(async () => { + window.location.reload(); + }, 2000); // Add a delay to allow the toast to show before redirecting + } + } + async function postRefresh(): Promise { // Get the refresh token from cookies + Cookies.remove('accessToken'); const refreshToken = Cookies.get('refreshToken'); if (!refreshToken) { + showErrorToast('Refresh token not found. Please log in again.'); return ''; } try { - const response = await apiCall<{ access_token: string }>('post', '/refresh', { - Cookie: 'refreshToken=' + refreshToken, - }); + const response = await apiCall<{ access_token: string }>('post', '/refresh', {}, {}, true, { refresh_token: refreshToken }); // Update the access token in cookies const accessToken = response.data.access_token; Cookies.set('accessToken', accessToken, { secure: true }); - // Update the refresh token in cookies - const cookieHeader = response.headers['set-cookie']; - if (cookieHeader) { - const refreshToken = cookieHeader[0].split(';')[0].split('=')[1]; - Cookies.set('refreshToken', refreshToken, { expires: 365, secure: true }); - } - return accessToken; } catch (error) { if (error instanceof axios.AxiosError) { @@ -273,13 +314,13 @@ function useApiClient() { } async function createConversationInvite(invitedUserName: string) { - const response = await apiCall('post', '/conversation', {}, { invitedUserName }); + const response = await apiCall('post', '/conversation', { Authorization: `Bearer ${Cookies.get('accessToken')}` }, { invitedUserName }); return response.data; } async function getAllConversations() { - const response = await apiCall<{ conversations: responses.GetAllConversations[] }>('get', '/conversations', {}); + const response = await apiCall<{ conversations: responses.GetAllConversations[] }>('get', '/conversations', { Authorization: `Bearer ${Cookies.get('accessToken')}` }, {}); return response.data; } @@ -291,7 +332,7 @@ function useApiClient() { } async function deleteConversation(conversationId: string) { - await apiCall('delete', '/conversation/' + conversationId, {}); + await apiCall('delete', '/conversation/' + conversationId, { Authorization: `Bearer ${Cookies.get('accessToken')}` }); } async function putSingleConversation(data: requests.PutSingleConversation) { @@ -490,7 +531,7 @@ function useApiClient() { postSharedSolutions, getAlignmentSummary, postConversationConsent, - + handleTokenRefresh, postUserBVisit, }; } From 19a6bbff6957fbc3e0f6affa057a6012efe9882e Mon Sep 17 00:00:00 2001 From: Sven Firmbach Date: Fri, 4 Oct 2024 12:29:15 +0200 Subject: [PATCH 60/61] Refactor useApiClient to set secure cookies for access and refresh tokens (#703) --- src/shared/hooks/useApiClient.tsx | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/src/shared/hooks/useApiClient.tsx b/src/shared/hooks/useApiClient.tsx index 839ba0aa..bfde08f7 100644 --- a/src/shared/hooks/useApiClient.tsx +++ b/src/shared/hooks/useApiClient.tsx @@ -37,7 +37,7 @@ function useApiClient() { // customCookies is used to set cookies (In this instance it is the refresh token) for the request and remove them after the request is done. if (customCookies) { Object.entries(customCookies).forEach(([key, value]) => { - Cookies.set(key, value, { secure: true, sameSite: 'strict' }); + Cookies.set(key, value, { secure: true }); }); } @@ -52,7 +52,7 @@ function useApiClient() { // If a new token is received, update it in the cookies and set the Authorization header if (newAccessToken) { accessToken = newAccessToken; - Cookies.set('accessToken', accessToken); + Cookies.set('accessToken', accessToken, { secure: true }); } else { showErrorToast('Your session has expired. Please login again.'); setTimeout(async () => { @@ -164,7 +164,7 @@ function useApiClient() { skipCaptcha: true, }; - const response = await apiCall('post', '/login', {}, body); + const response = await apiCall('post', '/login', {}, body, true); // Store the access token for userA in cookies if (isUserA) { @@ -172,24 +172,13 @@ function useApiClient() { Cookies.set('accessToken', accessToken, { secure: true }); } - // Set refresh token from response headers (if backend returns it) - const cookieHeader = response.headers['set-cookie']; - if (cookieHeader) { - const refreshToken = cookieHeader[0].split(';')[0].split('=')[1]; - Cookies.set('refreshToken', refreshToken, { expires: 365, secure: true }); - } return response.data; } async function postGoogleLogin(credential: string, quizId: string) { const response = await apiCall('post', '/auth/google', {}, { credential, quizId }, true); - const { access_token, refresh_token } = response.data; + const { access_token } = response.data; Cookies.set('accessToken', access_token, { secure: true }); - Cookies.set('refreshToken', refresh_token, { - secure: true, - sameSite: 'strict', - path: '/', - }); return response.data; } @@ -218,15 +207,9 @@ function useApiClient() { async function postRefresh(): Promise { // Get the refresh token from cookies Cookies.remove('accessToken'); - const refreshToken = Cookies.get('refreshToken'); - - if (!refreshToken) { - showErrorToast('Refresh token not found. Please log in again.'); - return ''; - } try { - const response = await apiCall<{ access_token: string }>('post', '/refresh', {}, {}, true, { refresh_token: refreshToken }); + const response = await apiCall<{ access_token: string }>('post', '/refresh', {}, {}, true); // Update the access token in cookies const accessToken = response.data.access_token; From 5f69cf899d039bb2965a6bc6dea2cfa700066998 Mon Sep 17 00:00:00 2001 From: Kirstie <39728053+epixieme@users.noreply.github.com> Date: Thu, 10 Oct 2024 20:17:09 +0100 Subject: [PATCH 61/61] 696 - Minor Fix on cypress to amend the wait times and failures (#706) * Refactor user authentication flow and improve UI consistency * Refactor scroll behavior for radar canvas in new user flow --- cypress/e2e/new-user-a.cy.ts | 98 +++++++++++++------ package.json | 2 +- .../UserAUnauthorizedPages/LoginPage.tsx | 5 +- .../UserAUnauthorizedPages/SignUpPage.tsx | 4 +- src/pages/UserBPages/UserBLoginPage.tsx | 6 +- src/pages/UserBPages/UserBSignUpPage.tsx | 7 +- 6 files changed, 78 insertions(+), 44 deletions(-) diff --git a/cypress/e2e/new-user-a.cy.ts b/cypress/e2e/new-user-a.cy.ts index 8befc9b3..960a413d 100644 --- a/cypress/e2e/new-user-a.cy.ts +++ b/cypress/e2e/new-user-a.cy.ts @@ -15,20 +15,26 @@ describe('New UserA', () => { it('allows new userA to go through 10 questions, see his results (personal values) and sign up', () => { // Load the HomePage and accept cookies cy.visit('/'); - cy.get('button').contains(/accept/i).click(); + cy.get('button') + .contains(/accept/i) + .click(); // Transition to the PreQuizPage through button click - cy.get('button').contains(/get started/i).click({ force: true }); + cy.get('button') + .contains(/get started/i) + .click({ force: true }); cy.location('pathname').should('equal', '/start'); // Transition to the QuizPage through button click - cy.get('button').contains(/take the quiz/i).click({ force: true }); + cy.get('button') + .contains(/take the quiz/i) + .click({ force: true }); cy.location('pathname').should('equal', '/questionnaire'); // Complete the quiz. for (let i = 0; i < 10; i++) { - cy.contains('Q' + (i+1)); - cy.contains('/10') + cy.contains('Q' + (i + 1)); + cy.contains('/10'); cy.contains(/not like me at all/i).click({ force: true }); cy.tick(300); // Skip the animation between questions cy.wait(100); // Wait for the next question to appear @@ -37,21 +43,32 @@ describe('New UserA', () => { // Submit the feedback and finish the quiz cy.contains(/bonus/i); cy.get('input').type('Some custom feedback'); - cy.get('button').contains(/finish quiz/i).click(); + cy.get('button') + .contains(/finish quiz/i) + .click(); // Find out the results (personal values) cy.location('pathname').should('equal', '/submit'); - cy.get('button').contains(/find out my climate personality/i).click(); + cy.get('button') + .contains(/find out my climate personality/i) + .click(); cy.location('pathname').should('equal', '/personal-values'); - - cy.contains(/power/i).should('be.visible'); - cy.contains(/security/i).should('be.visible'); - cy.contains(/tradition/i).should('be.visible'); - - cy.get('canvas[typeof="radar"]').should('be.visible'); + cy.tick(300); + cy.wait(100); + cy.contains(/power/i).scrollIntoView().should('be.visible'); + cy.contains(/security/i) + .scrollIntoView() + .should('be.visible'); + cy.contains(/tradition/i) + .scrollIntoView() + .should('be.visible'); + + cy.get('canvas[typeof="radar"]').scrollIntoView().should('be.visible'); // Complete by going to the sign up page - cy.get('button').contains(/dive in/i).click(); + cy.get('button') + .contains(/dive in/i) + .click(); cy.location('pathname').should('equal', '/sign-up'); // At first the sign up button should be disabled @@ -64,26 +81,34 @@ describe('New UserA', () => { cy.get('input[id="confirmPassword"]').type('asdf1234'); // Now the sign up button should be enabled and login the user as well - cy.get('button').contains(/create account/i).click(); + cy.get('button') + .contains(/create account/i) + .click(); cy.location('pathname').should('equal', '/climate-feed'); }); it('allows new userA to go through 20 questions, see his results (personal values) and sign up', () => { // Load the HomePage and accept cookies cy.visit('/'); - cy.get('button').contains(/accept/i).click(); + cy.get('button') + .contains(/accept/i) + .click(); // Transition to the PreQuizPage through button click - cy.get('button').contains(/get started/i).click({ force: true }); + cy.get('button') + .contains(/get started/i) + .click({ force: true }); cy.location('pathname').should('equal', '/start'); // Transition to the QuizPage through button click - cy.get('button').contains(/take the quiz/i).click({ force: true }); + cy.get('button') + .contains(/take the quiz/i) + .click({ force: true }); cy.location('pathname').should('equal', '/questionnaire'); // Complete the quiz. for (let i = 0; i < 10; i++) { - cy.contains('Q' + (i+1)); + cy.contains('Q' + (i + 1)); cy.contains('/10'); cy.contains(/not like me at all/i).click({ force: true }); cy.tick(300); // Skip the animation between questions @@ -93,16 +118,20 @@ describe('New UserA', () => { // Submit the feedback and finish the quiz cy.contains(/bonus/i); cy.get('input').type('Some custom feedback'); - cy.get('button').contains(/finish quiz/i).click(); + cy.get('button') + .contains(/finish quiz/i) + .click(); // Continue with the next 10 questions cy.location('pathname').should('equal', '/submit'); - cy.get('button').contains(/continue/i).click(); + cy.get('button') + .contains(/continue/i) + .click(); cy.location('pathname').should('equal', '/questionnaire'); // Complete the quiz. for (let i = 10; i < 20; i++) { - cy.contains('Q' + (i+1)); + cy.contains('Q' + (i + 1)); cy.contains('/20'); cy.contains(/not like me at all/i).click({ force: true }); cy.tick(300); // Skip the animation between questions @@ -111,17 +140,26 @@ describe('New UserA', () => { // Find out the results (personal values) cy.location('pathname').should('equal', '/submit-set-two'); - cy.get('button').contains(/find out my climate personality/i).click(); + cy.get('button') + .contains(/find out my climate personality/i) + .click(); cy.location('pathname').should('equal', '/personal-values'); - - cy.contains(/power/i).should('be.visible'); - cy.contains(/security/i).should('be.visible'); - cy.contains(/tradition/i).should('be.visible'); - - cy.get('canvas[typeof="radar"]').should('be.visible'); + cy.tick(300); + cy.wait(100); + cy.contains(/power/i).scrollIntoView().should('be.visible'); + cy.contains(/security/i) + .scrollIntoView() + .should('be.visible'); + cy.contains(/tradition/i) + .scrollIntoView() + .should('be.visible'); + + cy.get('canvas[typeof="radar"]').scrollIntoView().should('be.visible'); // Complete by going to the sign up page - cy.get('button').contains(/dive in/i).click(); + cy.get('button') + .contains(/dive in/i) + .click(); cy.location('pathname').should('equal', '/sign-up'); }); }); diff --git a/package.json b/package.json index cd0e01ac..d0413a9b 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "start": "react-scripts start", "build": "react-scripts build", "eject": "react-scripts eject", - "test": "cypress run", + "test": "cypress run --headed", "docker:build": "docker build -t react-docker .", "docker:run": "docker run -p 3000:80 -e http://localhost:5000 --rm react-docker", "docker:dev:build": "docker build -f Dockerfile.dev -t react-docker-dev .", diff --git a/src/pages/UserAUnauthorizedPages/LoginPage.tsx b/src/pages/UserAUnauthorizedPages/LoginPage.tsx index 79893084..7ca0e657 100644 --- a/src/pages/UserAUnauthorizedPages/LoginPage.tsx +++ b/src/pages/UserAUnauthorizedPages/LoginPage.tsx @@ -8,7 +8,6 @@ import { LoginForm, RequestPasswordResetModal, useLogin, useResetPassword } from import { useMobileView } from 'shared/hooks'; function LoginPage() { - const devMode = localStorage.getItem('devMode') === 'true'; const navigate = useNavigate(); const location = useLocation(); const isMobile = useMobileView(); @@ -50,8 +49,8 @@ function LoginPage() {
setShowPasswordResetModal(true)} /> - {devMode &&
} - {devMode && } +
+
setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> diff --git a/src/pages/UserAUnauthorizedPages/SignUpPage.tsx b/src/pages/UserAUnauthorizedPages/SignUpPage.tsx index 82a21652..c30d67b2 100644 --- a/src/pages/UserAUnauthorizedPages/SignUpPage.tsx +++ b/src/pages/UserAUnauthorizedPages/SignUpPage.tsx @@ -10,8 +10,6 @@ import { SignUpForm, useSignUp } from 'features/auth'; import GoogleLogin from 'features/auth/components/GoogleLogin'; function SignUpPage() { - const devMode = localStorage.getItem('devMode') === 'true'; - const signUpId = uuidv4(); const navigate = useNavigate(); @@ -51,7 +49,7 @@ function SignUpPage() {
- {devMode && } +
diff --git a/src/pages/UserBPages/UserBLoginPage.tsx b/src/pages/UserBPages/UserBLoginPage.tsx index 143b9759..77d6aa5b 100644 --- a/src/pages/UserBPages/UserBLoginPage.tsx +++ b/src/pages/UserBPages/UserBLoginPage.tsx @@ -8,8 +8,6 @@ import { useMobileView } from 'shared/hooks'; import GoogleLogin from 'features/auth/components/GoogleLogin'; function UserBLoginPage() { - const devMode = localStorage.getItem('devMode') === 'true'; - const navigate = useNavigate(); const { conversationId } = useParams(); const isMobile = useMobileView(); @@ -52,8 +50,8 @@ function UserBLoginPage() { Climate Mind Logo
setShowPasswordResetModal(true)} /> - {devMode &&
} - {devMode && } +
+
setShowPasswordResetModal(false)} onSubmit={handlePasswordReset} /> diff --git a/src/pages/UserBPages/UserBSignUpPage.tsx b/src/pages/UserBPages/UserBSignUpPage.tsx index d5979529..d4373eb7 100644 --- a/src/pages/UserBPages/UserBSignUpPage.tsx +++ b/src/pages/UserBPages/UserBSignUpPage.tsx @@ -20,7 +20,6 @@ function UserBSignUpPage() { const { sessionId, quizId } = useAppSelector((state) => state.auth.userB); const { signUp } = useSignUp(); const [isLoading, setIsLoading] = useState(false); - const devMode = localStorage.getItem('devMode') === 'true'; async function signUpHandler(firstName: string, lastName: string, email: string, password: string) { setIsLoading(true); @@ -48,13 +47,15 @@ function UserBSignUpPage() { Welcome to Climate Mind
- Already have an account? + + Already have an account? + navigate(ROUTES.LOGIN_PAGE)} style={styles.loginButton} />
- {devMode && } +