Skip to content

Commit

Permalink
[FEATURE]: New register schema, helpertext update on form (#222)
Browse files Browse the repository at this point in the history
* feat: register schema changed,checkbox removed

* test: ways of changing password helpertext

* test: new input approach for helpertext

* fix: paddings, helperText, password schema and onBlur forms

* fix: applying requested changes from PR review

* fix: backend schema update
  • Loading branch information
f-morgado authored Jun 2, 2022
1 parent 22873c8 commit 26f599a
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 86 deletions.
4 changes: 2 additions & 2 deletions backend/src/modules/users/dto/create.user.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ export default class CreateUserDto {

@IsString()
@IsNotEmpty()
@MinLength(3)
@MinLength(2)
@Transform(({ value }: TransformFnParams) => value.trim())
firstName!: string;

@IsString()
@IsNotEmpty()
@MinLength(3)
@MinLength(2)
@Transform(({ value }: TransformFnParams) => value.trim())
lastName!: string;

Expand Down
19 changes: 12 additions & 7 deletions frontend/components/Primitives/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,14 @@ const IconWrapper = styled(Flex, {

const HelperTextWrapper = styled(Flex, {
'& svg': {
height: '$16 !important',
width: '$16 !important',
flex: '0 0 16px',
// transform: 'transformY(100%)',
// mt: '0px',
height: '$16 ',
width: '$16 ',
color: '$dangerBase'
}
},
'& *:not(svg)': { flex: '1 1 auto' }
});

const StyledInput = styled('input', {
Expand Down Expand Up @@ -225,11 +229,12 @@ const Input: React.FC<InputProps> = ({
const message = errors[id]?.message;
const value = getValues()[id];
const isValueEmpty = isEmpty(value);

const autoState = useMemo(() => {
// console.log('!message=', !message);
if (message) return 'error';
if (isValueEmpty) return 'default';
return 'valid';
if (!message && !isValueEmpty) return 'valid';
return undefined;
}, [message, isValueEmpty]);

const currentState = useMemo(() => {
Expand All @@ -256,7 +261,7 @@ const Input: React.FC<InputProps> = ({
css={{ position: 'relative', width: '100%', mb: '$16', height: 'auto', ...css }}
onBlur={() => {
if (isValueEmpty) {
clearErrors();
clearErrors(id);
}
}}
>
Expand Down Expand Up @@ -300,7 +305,7 @@ const Input: React.FC<InputProps> = ({
</Flex>
<Flex justify={!isHelperEmpty ? 'between' : 'end'}>
{!isHelperEmpty && (
<HelperTextWrapper gap="4" align="center" css={{ mt: '$8' }}>
<HelperTextWrapper gap="4" css={{ mt: '$8' }}>
{currentState === 'error' && (
<Icon name="info" css={{ width: '$24', height: '$24' }} />
)}
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/auth/LoginForm/styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ const OrSeparator = styled('div', {

width: '100%',

mt: '$24',
mb: '$32',
mt: '$26',
mb: '$34',

hr: {
flexGrow: 1,
Expand Down
50 changes: 21 additions & 29 deletions frontend/components/auth/SignUp/RegisterForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { AxiosError } from 'axios';
import React, { Dispatch, useState } from 'react';
import router from 'next/router';
import { RedirectableProviderType } from 'next-auth/providers';
import { signIn } from 'next-auth/react';
import React, { Dispatch } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { useMutation } from 'react-query';
import { useSetRecoilState } from 'recoil';
Expand All @@ -11,11 +14,11 @@ import { styled } from '../../../stitches.config';
import { toastState } from '../../../store/toast/atom/toast.atom';
import { RegisterUser, User } from '../../../types/user/user';
import { ToastStateEnum } from '../../../utils/enums/toast-types';
import { DASHBOARD_ROUTE } from '../../../utils/routes';
import { SignUpEnum } from '../../../utils/signUp.enum';
import Icon from '../../icons/Icon';
import LogoIcon from '../../icons/Logo';
import Button from '../../Primitives/Button';
import CheckBox from '../../Primitives/Checkbox';
import Flex from '../../Primitives/Flex';
import Input from '../../Primitives/Input';
import Text from '../../Primitives/Text';
Expand All @@ -38,17 +41,11 @@ interface RegisterFormProps {
setEmailName: Dispatch<React.SetStateAction<{ email: string; goback: boolean }>>;
}

const RegisterForm: React.FC<RegisterFormProps> = ({
setShowSignUp,
emailName,
setCurrentTab,
setEmailName
}) => {
const RegisterForm: React.FC<RegisterFormProps> = ({ setShowSignUp, emailName, setEmailName }) => {
const setToastState = useSetRecoilState(toastState);
const [checkedTerms, setCheckedTerms] = useState(false);
const methods = useForm<RegisterUser>({
mode: 'onChange',
reValidateMode: 'onChange',
mode: 'onBlur',
reValidateMode: 'onBlur',
defaultValues: {
email: '',
firstName: '',
Expand All @@ -67,6 +64,16 @@ const RegisterForm: React.FC<RegisterFormProps> = ({
clearErrors();
setShowSignUp(SignUpEnum.SIGN_UP);
};
const handleLogin = async () => {
const result = await signIn<RedirectableProviderType>('credentials', {
...methods.getValues(),
callbackUrl: DASHBOARD_ROUTE,
redirect: false
});
if (!result?.error) {
router.push(DASHBOARD_ROUTE);
}
};

const createUser = useMutation<User, AxiosError, RegisterUser, unknown>(
(user: RegisterUser) => registerNewUser(user),
Expand All @@ -80,13 +87,13 @@ const RegisterForm: React.FC<RegisterFormProps> = ({
});
},
onSuccess: () => {
setShowSignUp(SignUpEnum.SIGN_UP);
setCurrentTab('login');
handleLogin();
}
}
);

const handleRegister = async (user: RegisterUser) => {
user.email = user.email.toLowerCase();
createUser.mutate(user);
};

Expand All @@ -97,14 +104,6 @@ const RegisterForm: React.FC<RegisterFormProps> = ({
direction="column"
style={{ width: '100%' }}
onSubmit={methods.handleSubmit((credentials: RegisterUser) => {
if (!checkedTerms) {
setToastState({
open: true,
type: ToastStateEnum.ERROR,
content: 'Confirm Terms of Service and Privacy Policy'
});
return;
}
handleRegister(credentials);
})}
>
Expand Down Expand Up @@ -133,6 +132,7 @@ const RegisterForm: React.FC<RegisterFormProps> = ({
type="password"
icon="eye"
iconPosition="right"
helperText="Use at least 8 characters, upper and lower case letters, numbers and symbols like !”?$%^&)."
/>
<Input
id="passwordConf"
Expand All @@ -141,14 +141,6 @@ const RegisterForm: React.FC<RegisterFormProps> = ({
icon="eye"
iconPosition="right"
/>
<Flex>
<CheckBox
id="checkbox"
label="I agree to the Terms of Service and the Privacy Policy."
size="16"
setCheckedTerms={setCheckedTerms}
/>
</Flex>
<Button
type="submit"
size="lg"
Expand Down
10 changes: 3 additions & 7 deletions frontend/components/auth/SignUp/SignUpOptionsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,20 @@ const SignUpOptionsForm: React.FC<SignUpOptionsFormProps> = ({
return setShowSignUp(SignUpEnum.SIGN_UP);
};

// const [ShowLogIn, setShowLogIn] = useState(false);
return (
<Container direction="column">
<Text size="md">
The email
<Text size="md" fontWeight="medium">
{` ${emailName} `}
{emailName}
</Text>
{/* {`${textContent}`} */}
supports login with company SSO (Single Sign-on)
</Text>
<Button
type="submit"
size="lg"
css={{
mt: '$32',
mb: '$22',
fontWeight: '$medium',
fontSize: '$18',
'& svg': {
Expand All @@ -56,9 +53,9 @@ const SignUpOptionsForm: React.FC<SignUpOptionsFormProps> = ({
}}
onClick={loginAzure}
>
Log in
Log in with SSO
</Button>
<OrSeparator>
<OrSeparator css={{ mt: '$22', mb: '$22' }}>
<hr />
<Text size="sm" color="primary300" weight="medium">
or
Expand All @@ -71,7 +68,6 @@ const SignUpOptionsForm: React.FC<SignUpOptionsFormProps> = ({
underline="true"
css={{
alignSelf: 'center',
mt: '$22',
'&:hover': {
textDecorationLine: 'underline',
cursor: 'pointer'
Expand Down
34 changes: 3 additions & 31 deletions frontend/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,11 @@ import LoginForm from '../components/auth/LoginForm';
import SignUpTabContent from '../components/auth/SignUp/SignUpTabContent';
import TroubleLogin from '../components/auth/TroubleLogin';
import Banner from '../components/icons/Banner';
import Flex from '../components/Primitives/Flex';
import { TabsList, TabsRoot, TabsTrigger } from '../components/Primitives/Tab';
import Text from '../components/Primitives/Text';
import { styled } from '../stitches.config';
import { BannerContainer, CenteredContainer, ImageBackground } from '../styles/pages/auth.styles';
import { DASHBOARD_ROUTE } from '../utils/routes';

const CenteredContainer = styled(Flex, {
position: 'absolute',
top: '$202',
right: '$162',
boxSizing: 'border-box',
'@media (max-height: 1023px)': {
top: 'calc((100vh - 710px) / 2)'
},
'&:focus': { outline: 'none' }
});

const MainContainer = styled(Flex, {
height: '100vh',
width: '100%',
position: 'relative',
backgroundColor: '$black',
backgroundImage: 'url(images/background.svg)',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat'
});

const BannerContainer = styled(Flex, {
mt: '$72',
ml: '$112',
size: 'fit-content'
});

export const getServerSideProps: GetServerSideProps = async (ctx) => {
const session = await getSession(ctx);
if (session) {
Expand All @@ -56,7 +28,7 @@ const Home: NextPage = () => {
const [currentTab, setCurrentTab] = useState('login');
const [showTroubleLogin, setShowTroubleLogin] = useState(false);
return (
<MainContainer>
<ImageBackground>
<BannerContainer>
<Banner />
</BannerContainer>
Expand All @@ -81,7 +53,7 @@ const Home: NextPage = () => {
)}
{showTroubleLogin && <TroubleLogin setShowTroubleLogin={setShowTroubleLogin} />}
</CenteredContainer>
</MainContainer>
</ImageBackground>
);
};

Expand Down
15 changes: 7 additions & 8 deletions frontend/schema/schemaRegisterForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,19 @@ const schemaRegisterForm = z
firstName: z
.string()
.nonempty('Please enter your name.')
.min(3, 'Your name must have more than 3 characters.'),
.min(2, 'Your name must have more than 2 characters.'),
lastName: z
.string()
.nonempty('Please enter your name.')
.min(3, 'Your name must have more than 3 characters.'),
.min(2, 'Your name must have more than 2 characters.'),
email: z.string().nonempty('Please insert your email.').email('This email is not valid.'),
password: z
.string()
.nonempty('Please enter your password.')
.regex(/.*[A-Z].*/, 'One uppercase character')
.regex(/.*[a-z].*/, 'One lowercase character')
.regex(/.*\d.*/, 'One number')
.regex(/.*[`~<>?,.\/!@#$%^&*()\-_+="'|{}\[\];:\\].*/, 'One special character')
.min(8, 'Password must be at least 8 characters.'),
.regex(
/^(?=.*[A-Za-z])(?=.*\d)(?=.*\W)[A-Za-z\d\W]{8,}$/,
'Use at least 8 characters, upper and lower case letters, numbers and symbols like !”?$%^&).'
),

passwordConf: z.string().nonempty('Please enter a valid password.')
})
.refine((data) => data.password === data.passwordConf, {
Expand Down
38 changes: 38 additions & 0 deletions frontend/styles/pages/auth.styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Flex from '../../components/Primitives/Flex';
import { styled } from '../../stitches.config';

const CenteredContainer = styled('div', {
position: 'absolute',
top: '50%',
right: '150px',

transform: 'translateY(-50%)',

maxWidth: '500px',
height: 'fit-content',

display: 'flex',
flexDirection: 'column',

backgroundColor: '#ffffff',
borderRadius: '$12'
});
const ImageBackground = styled(Flex, {
height: '100vh',
width: '100%',
position: 'relative',
backgroundColor: '$black',
backgroundImage: 'url(images/background.svg)',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat'
});

const BannerContainer = styled(Flex, {
size: 'fit-content',

position: 'absolute',
left: '112px',
top: '72px'
});

export { BannerContainer, CenteredContainer, ImageBackground };

0 comments on commit 26f599a

Please sign in to comment.