Skip to content
This repository has been archived by the owner on Nov 21, 2024. It is now read-only.

Commit

Permalink
feat: show differnt success message for password on success, refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
pyphilia authored and ReidyT committed Aug 21, 2024
1 parent bac130a commit 35afffc
Show file tree
Hide file tree
Showing 14 changed files with 440 additions and 382 deletions.
2 changes: 1 addition & 1 deletion cypress/e2e/SignIn.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe('Name and Email Validation', () => {
cy.visit(SIGN_IN_PATH);
// Signing in with a wrong email format
cy.signInAndCheck(WRONG_EMAIL);
// Siging in with a valid email
// Signing in with a valid email
cy.signInAndCheck(GRAASP);
});
});
25 changes: 25 additions & 0 deletions cypress/e2e/SignInPassword.cy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { API_ROUTES } from '@graasp/query-client';

import { SIGN_IN_PATH } from '../../src/config/paths';
import { PASSWORD_SUCCESS_ALERT } from '../../src/config/selectors';
import { MEMBERS } from '../fixtures/members';

describe('Email and Password Validation', () => {
Expand Down Expand Up @@ -28,4 +29,28 @@ describe('Email and Password Validation', () => {

cy.url().should('contain', redirectionLink);
});

it('Sign In With Password shows success message if no redirect', () => {
cy.intercept(
{
pathname: API_ROUTES.SIGN_IN_WITH_PASSWORD_ROUTE,
},
(req) => {
req.reply({ statusCode: 303 });
},
).as('signInWithPassword');

const { WRONG_EMAIL, WRONG_PASSWORD, GRAASP } = MEMBERS;
cy.visit(SIGN_IN_PATH);
// Select sign in method
cy.signInPasswordMethodAndCheck();
// Signing in with wrong email
cy.signInPasswordAndCheck(WRONG_EMAIL);
// Signing in with a valid email but empty password
cy.signInPasswordAndCheck(WRONG_PASSWORD);
// Signing in with a valid email and password
cy.signInPasswordAndCheck(GRAASP);

cy.get(`#${PASSWORD_SUCCESS_ALERT}`).should('be.visible');
});
});
8 changes: 0 additions & 8 deletions cypress/e2e/SuccessContent.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { SIGN_IN_PATH, SIGN_UP_PATH } from '../../src/config/paths';
import {
BACK_BUTTON_ID,
EMAIL_SIGN_IN_FIELD_ID,
EMAIL_SIGN_UP_FIELD_ID,
RESEND_EMAIL_BUTTON_ID,
SIGN_IN_HEADER_ID,
SIGN_UP_HEADER_ID,
Expand Down Expand Up @@ -105,8 +104,6 @@ describe('Success Content', () => {
cy.get(`#${SUCCESS_CONTENT_ID}`).should('not.exist');
cy.url().should('include', SIGN_UP_PATH);
cy.get(`#${SIGN_UP_HEADER_ID}`).should('be.visible');
// checks so email is still filled
cy.get(`#${EMAIL_SIGN_UP_FIELD_ID}`).should('have.value', GRAASP.email);

// check if it's possible to sign up and use back button again
cy.signUpAndCheck(GRAASP_OTHER, true);
Expand All @@ -118,11 +115,6 @@ describe('Success Content', () => {
cy.get(`#${SUCCESS_CONTENT_ID}`).should('not.exist');
cy.url().should('include', SIGN_UP_PATH);
cy.get(`#${SIGN_UP_HEADER_ID}`).should('be.visible');
// checks so email is still filled
cy.get(`#${EMAIL_SIGN_UP_FIELD_ID}`).should(
'have.value',
GRAASP_OTHER.email,
);
});

it('Resend email', () => {
Expand Down
6 changes: 6 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import * as Sentry from '@sentry/react';
import { Route, BrowserRouter as Router, Routes } from 'react-router-dom';

import ErrorFallback from './components/ErrorFallback';
import MagicLinkSuccessContent from './components/MagicLinkSuccessContent';
import MobileAuth from './components/MobileAuth';
import Redirection from './components/Redirection';
import SignIn from './components/SignIn';
import SignUp from './components/SignUp';
import {
HOME_PATH,
MOBILE_AUTH_PATH,
SIGN_IN_MAGIC_LINK_SUCCESS_PATH,
SIGN_IN_PATH,
SIGN_UP_PATH,
} from './config/paths';
Expand All @@ -19,6 +21,10 @@ const App = () => (
<Redirection>
<Routes>
<Route path={SIGN_IN_PATH} element={<SignIn />} />
<Route
path={SIGN_IN_MAGIC_LINK_SUCCESS_PATH}
element={<MagicLinkSuccessContent />}
/>
<Route path={SIGN_UP_PATH} element={<SignUp />} />
<Route path={HOME_PATH} element={<SignIn />} />
<Route path={MOBILE_AUTH_PATH} element={<MobileAuth />} />
Expand Down
107 changes: 107 additions & 0 deletions src/components/MagicLinkForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router';

import { RecaptchaAction } from '@graasp/sdk';

import { LoadingButton } from '@mui/lab';

import { useAuthTranslation } from '../config/i18n';
import { SIGN_IN_MAGIC_LINK_SUCCESS_PATH } from '../config/paths';
import { mutations } from '../config/queryClient';
import { EMAIL_SIGN_IN_FIELD_ID, SIGN_IN_BUTTON_ID } from '../config/selectors';
import { useRecaptcha } from '../context/RecaptchaContext';
import { useMobileAppLogin } from '../hooks/mobile';
import { useRedirection } from '../hooks/searchParams';
import { AUTH } from '../langs/constants';
import { emailValidator } from '../utils/validation';
import EmailInput from './EmailInput';
import ErrorDisplay from './common/ErrorDisplay';

const { SIGN_IN_BUTTON } = AUTH;

const MagicLinkForm = () => {
const navigate = useNavigate();
const { t } = useAuthTranslation();
const { executeCaptcha } = useRecaptcha();

const { isMobile, challenge } = useMobileAppLogin();
const redirect = useRedirection();

const [email, setEmail] = useState('');

// enable validation after first click
const [shouldValidate, setShouldValidate] = useState(false);

const {
mutateAsync: signIn,
isLoading: isLoadingSignIn,
error: webSignInError,
} = mutations.useSignIn();
const {
mutateAsync: mobileSignIn,
isLoading: isLoadingMobileSignIn,
error: mobileSignInError,
} = mutations.useMobileSignIn();

const signInError = webSignInError || mobileSignInError;

const handleSignIn = async () => {
const lowercaseEmail = email.toLowerCase();
const checkingEmail = emailValidator(lowercaseEmail);
if (checkingEmail) {
setShouldValidate(true);
} else {
try {
const token = await executeCaptcha(
isMobile ? RecaptchaAction.SignInMobile : RecaptchaAction.SignIn,
);
await (isMobile
? mobileSignIn({ email: lowercaseEmail, captcha: token, challenge })
: signIn({
email: lowercaseEmail,
captcha: token,
url: redirect.url,
}));

// navigate to success path
navigate({
pathname: SIGN_IN_MAGIC_LINK_SUCCESS_PATH,
search: `email=${email}`,
});
} catch (e) {
console.error(e);
}
}
};

const handleKeypress = (e: React.KeyboardEvent<HTMLInputElement>) => {
// signInMethod email when true
// sign in by pressing the enter key
if (e.key === 'Enter') {
handleSignIn();
}
};

return (
<>
<EmailInput
value={email}
setValue={setEmail}
id={EMAIL_SIGN_IN_FIELD_ID}
onKeyPress={handleKeypress}
shouldValidate={shouldValidate}
/>
<ErrorDisplay error={signInError} />
<LoadingButton
id={SIGN_IN_BUTTON_ID}
variant="contained"
onClick={handleSignIn}
loading={isLoadingMobileSignIn || isLoadingSignIn}
>
{t(SIGN_IN_BUTTON)}
</LoadingButton>
</>
);
};

export default MagicLinkForm;
106 changes: 106 additions & 0 deletions src/components/MagicLinkSuccessContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { useState } from 'react';
import { Trans } from 'react-i18next';
import { useNavigate, useSearchParams } from 'react-router-dom';

import { RecaptchaAction } from '@graasp/sdk';
import { namespaces } from '@graasp/translations';
import { Button } from '@graasp/ui';

import MailOutlineIcon from '@mui/icons-material/MailOutline';
import { Container, Stack, Typography } from '@mui/material';

import { useAuthTranslation } from '../config/i18n';
import { mutations } from '../config/queryClient';
import {
BACK_BUTTON_ID,
RESEND_EMAIL_BUTTON_ID,
SUCCESS_CONTENT_ID,
} from '../config/selectors';
import { useRecaptcha } from '../context/RecaptchaContext';
import { useRedirection } from '../hooks/searchParams';
import { AUTH } from '../langs/constants';
import FullscreenContainer from './FullscreenContainer';

const MagicLinkSuccessContent = () => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const { t } = useAuthTranslation();
const { executeCaptcha } = useRecaptcha();
const [isEmailSent, setIsEmailSent] = useState(false);

// used for resend email
const redirect = useRedirection();
const { mutate: signIn } = mutations.useSignIn();

const email = searchParams.get('email');

// used for resend email
const handleResendEmail = async () => {
const lowercaseEmail = email.toLowerCase();
const token = await executeCaptcha(RecaptchaAction.SignIn);
signIn({
email: lowercaseEmail,
captcha: token,
url: redirect.url,
});
};

const onClickResendEmail = () => {
setIsEmailSent(true);
handleResendEmail();
};

const handleBackButtonClick = () => {
navigate(-1);
};

return (
<FullscreenContainer>
<Container id={SUCCESS_CONTENT_ID} maxWidth="sm">
<Stack direction="column" spacing={2}>
<Typography
variant="h4"
display="flex"
justifyContent="center"
alignItems="center"
>
<MailOutlineIcon fontSize="large" sx={{ marginRight: 1 }} />
{t(AUTH.SIGN_IN_SUCCESS_TITLE)}
</Typography>
<Typography variant="body1" align="justify">
<Trans
ns={namespaces.auth}
i18nKey={AUTH.SIGN_IN_SUCCESS_TEXT}
values={{ email }}
components={{ bold: <strong /> }}
/>
</Typography>
<Typography variant="body1" align="justify">
{t(AUTH.SIGN_IN_SUCCESS_EMAIL_PROBLEM)}
</Typography>
<Stack direction="row" justifyContent="center" spacing={1}>
<Button
variant="text"
color="primary"
id={BACK_BUTTON_ID}
onClick={handleBackButtonClick}
>
{t(AUTH.BACK_BUTTON)}
</Button>
<Button
variant="outlined"
color="primary"
onClick={onClickResendEmail}
id={RESEND_EMAIL_BUTTON_ID}
disabled={isEmailSent}
>
{t(AUTH.RESEND_EMAIL_BUTTON)}
</Button>
</Stack>
</Stack>
</Container>
</FullscreenContainer>
);
};

export default MagicLinkSuccessContent;
Loading

0 comments on commit 35afffc

Please sign in to comment.