Skip to content

Commit

Permalink
feat: add request password request and reset pages
Browse files Browse the repository at this point in the history
  • Loading branch information
pyphilia committed May 27, 2024
1 parent c96b454 commit 72cebc6
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 1 deletion.
6 changes: 6 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ import * as Sentry from '@sentry/react';
import { Route, BrowserRouter as Router, Routes } from 'react-router-dom';

import ErrorFallback from './components/ErrorFallback';
import ForgotPassword from './components/ForgotPassword';
import MagicLinkSuccessContent from './components/MagicLinkSuccessContent';
import MobileAuth from './components/MobileAuth';
import Redirection from './components/Redirection';
import ResetPassword from './components/ResetPassword';
import SignIn from './components/SignIn';
import SignUp from './components/SignUp';
import {
FORGOT_PASSWORD_PATH,
HOME_PATH,
MOBILE_AUTH_PATH,
RESET_PASSWORD_PATH,
SIGN_IN_MAGIC_LINK_SUCCESS_PATH,
SIGN_IN_PATH,
SIGN_UP_PATH,
Expand All @@ -26,6 +30,8 @@ const App = () => (
element={<MagicLinkSuccessContent />}
/>
<Route path={SIGN_UP_PATH} element={<SignUp />} />
<Route path={FORGOT_PASSWORD_PATH} element={<ForgotPassword />} />
<Route path={RESET_PASSWORD_PATH} element={<ResetPassword />} />
<Route path={HOME_PATH} element={<SignIn />} />
<Route path={MOBILE_AUTH_PATH} element={<MobileAuth />} />
</Routes>
Expand Down
64 changes: 64 additions & 0 deletions src/components/ForgotPassword.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { useState } from 'react';
import { Link } from 'react-router-dom';

import { Button, GraaspLogo } from '@graasp/ui';

import { Stack, useTheme } from '@mui/material';
import FormControl from '@mui/material/FormControl';
import Typography from '@mui/material/Typography';

import { useAuthTranslation } from '../config/i18n';
import { SIGN_IN_PATH } from '../config/paths';
import { AUTH } from '../langs/constants';
import EmailInput from './EmailInput';
import FullscreenContainer from './FullscreenContainer';

const ForgotPassword = () => {
const { t } = useAuthTranslation();
const theme = useTheme();

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

const resetPassword = () => {
setShouldValidate(true);
};

const handleKeypress = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
resetPassword();
}
};

return (
<FullscreenContainer>
{
<Stack direction="column" alignItems="center" spacing={2}>
<Stack spacing={1}>
<GraaspLogo height={90} sx={{ fill: theme.palette.primary.main }} />
<Typography variant="h4" component="h2">
{t(AUTH.FORGOT_PASSWORD_TITLE)}
</Typography>
</Stack>
<FormControl>
<Stack spacing={1}>
<EmailInput
value={email}
setValue={setEmail}
onKeyPress={handleKeypress}
shouldValidate={shouldValidate}
/>
</Stack>
</FormControl>
<Button fullWidth onClick={resetPassword}>
{t(AUTH.FORGOT_PASSWORD_BUTTON)}
</Button>
<Link to={SIGN_IN_PATH}>{t(AUTH.FORGOT_PASSWORD_BACK_BUTTON)}</Link>
</Stack>
}
</FullscreenContainer>
);
};

export default ForgotPassword;
123 changes: 123 additions & 0 deletions src/components/ResetPassword.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { useState } from 'react';
import { Link } from 'react-router-dom';

import { Button, GraaspLogo } from '@graasp/ui';

import { Stack, useTheme } from '@mui/material';
import FormControl from '@mui/material/FormControl';
import Typography from '@mui/material/Typography';

import { useAuthTranslation } from '../config/i18n';
import { SIGN_IN_PATH } from '../config/paths';
import { AUTH } from '../langs/constants';
import { passwordValidator } from '../utils/validation';
import FullscreenContainer from './FullscreenContainer';
import StyledTextField from './StyledTextField';

const ResetPassword = () => {
const { t } = useAuthTranslation();
const theme = useTheme();

// enable validation after first click
const [passwordError, setPasswordError] = useState<string | null>(null);
const [confirmPasswordError, setConfirmPasswordError] = useState<
string | null
>(null);
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');

const resetPassword = () => {
const checkingPassword = passwordValidator(password);
const checkingConfirmPassword = passwordValidator(confirmPassword);
if (checkingPassword || checkingConfirmPassword) {
if (checkingPassword) {
setPasswordError(checkingPassword);
}
if (checkingConfirmPassword) {
setConfirmPasswordError(checkingConfirmPassword);
}
} else {
setPasswordError(null);
setConfirmPasswordError(null);
// const token = await executeCaptcha(
// isMobile
// ? RecaptchaAction.SignInWithPasswordMobile
// : RecaptchaAction.SignInWithPassword,
// );
// const result = await (isMobile
// ? mobileSignInWithPassword({
// email: lowercaseEmail,
// password,
// captcha: token,
// challenge,
// })
// : signInWithPassword({
// email: lowercaseEmail,
// password,
// captcha: token,
// url: redirect.url,
// }));
// // successful redirect
// if (result && result.resource) {
// window.location.href = result.resource;
// }
}
};

const handleKeypress = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
resetPassword();
}
};

return (
<FullscreenContainer>
{
<Stack direction="column" alignItems="center" spacing={2}>
<Stack spacing={1}>
<GraaspLogo height={90} sx={{ fill: theme.palette.primary.main }} />
<Typography variant="h4" component="h2">
{t(AUTH.RESET_PASSWORD_TITLE)}
</Typography>
</Stack>
<FormControl>
<Stack spacing={1}>
<StyledTextField
required
label={t(AUTH.PASSWORD_FIELD_LABEL)}
variant="outlined"
value={password}
error={Boolean(passwordError)}
helperText={t(passwordError)}
onChange={(e) => {
setPassword(e.target.value);
}}
type="password"
onKeyDown={handleKeypress}
/>
<StyledTextField
required
label={t(AUTH.CONFIRM_PASSWORD_FIELD_LABEL)}
variant="outlined"
value={confirmPassword}
error={Boolean(confirmPasswordError)}
helperText={t(confirmPasswordError)}
onChange={(e) => {
setConfirmPassword(e.target.value);
}}
type="password"
onKeyDown={handleKeypress}
/>
</Stack>
</FormControl>
<Button fullWidth onClick={resetPassword}>
{t(AUTH.FORGOT_PASSWORD_BUTTON)}
</Button>
<Link to={SIGN_IN_PATH}>{t(AUTH.FORGOT_PASSWORD_BACK_BUTTON)}</Link>
</Stack>
}
</FullscreenContainer>
);
};

export default ResetPassword;
3 changes: 3 additions & 0 deletions src/components/SignInPasswordForm.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React, { useState } from 'react';
import { Link } from 'react-router-dom';

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

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

import { useAuthTranslation } from '../config/i18n';
import { FORGOT_PASSWORD_PATH } from '../config/paths';
import { mutations } from '../config/queryClient';
import {
EMAIL_SIGN_IN_FIELD_ID,
Expand Down Expand Up @@ -131,6 +133,7 @@ const SignInPasswordForm = () => {
>
{t(SIGN_IN_BUTTON)}
</LoadingButton>
<Link to={`${FORGOT_PASSWORD_PATH}`}>{t(AUTH.FORGOT_PASSWORD_LINK)}</Link>
{(signInWithPasswordSuccess || mobileSignInWithPasswordSuccess) && (
<Alert severity="success" id={PASSWORD_SUCCESS_ALERT}>
{t(AUTH.PASSWORD_SUCCESS_ALERT)}
Expand Down
2 changes: 2 additions & 0 deletions src/config/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ export const SIGN_IN_MAGIC_LINK_SUCCESS_PATH = '/signin/success';
export const SIGN_UP_PATH = '/signup';
export const HOME_PATH = '/';
export const MOBILE_AUTH_PATH = '/auth';
export const FORGOT_PASSWORD_PATH = '/password/forgot';
export const RESET_PASSWORD_PATH = '/password/reset';
6 changes: 6 additions & 0 deletions src/langs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,10 @@ export const AUTH = {
EMPTY_EMAIL_ERROR: 'EMPTY_EMAIL_ERROR',
PASSWORD_EMPTY_ERROR: 'PASSWORD_EMPTY_ERROR',
PASSWORD_SUCCESS_ALERT: 'PASSWORD_SUCCESS_ALERT',
FORGOT_PASSWORD_LINK: 'FORGOT_PASSWORD_LINK',
FORGOT_PASSWORD_TITLE: 'FORGOT_PASSWORD_TITLE',
FORGOT_PASSWORD_BUTTON: 'FORGOT_PASSWORD_BUTTON',
FORGOT_PASSWORD_BACK_BUTTON: 'FORGOT_PASSWORD_BACK_BUTTON',
RESET_PASSWORD_TITLE: 'RESET_PASSWORD_TITLE',
CONFIRM_PASSWORD_FIELD_LABEL: 'CONFIRM_PASSWORD_FIELD_LABEL',
};
8 changes: 7 additions & 1 deletion src/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,11 @@
"INVALID_EMAIL_ERROR": "This does not look like a valid email address",
"EMPTY_EMAIL_ERROR": "An email address is required, this field can not be empty",
"PASSWORD_EMPTY_ERROR": "The password can not be empty",
"PASSWORD_SUCCESS_ALERT": "You successfully signed in. You will be redirected soon."
"PASSWORD_SUCCESS_ALERT": "You successfully signed in. You will be redirected soon.",
"FORGOT_PASSWORD_LINK": "Forgot your password? Click here to reset",
"FORGOT_PASSWORD_TITLE": "Request new password",
"FORGOT_PASSWORD_BUTTON": "Reset",
"FORGOT_PASSWORD_BACK_BUTTON": "Back to sign in",
"RESET_PASSWORD_TITLE": "Reset your password",
"CONFIRM_PASSWORD_FIELD_LABEL": "Confirm password"
}

0 comments on commit 72cebc6

Please sign in to comment.