Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DEV-896]: Sign up page #345

Merged
merged 23 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a4aa6e4
feat(DEV-896): add sign up page
MikeAtUqido Oct 23, 2023
01134ae
feat(DEV-896): update sign up page
MikeAtUqido Oct 23, 2023
886c528
Merge branch 'main' into feature/DEV-896-sign-up
MarcoPonchia Oct 31, 2023
b313b62
feat(DEV-896): update sign-up page
MikeAtUqido Nov 2, 2023
2a7d098
Merge remote-tracking branch 'origin/main' into feature/DEV-896-sign-up
MikeAtUqido Nov 2, 2023
35fb16a
feat(DEV-896): add RequiredTextField component
MikeAtUqido Nov 2, 2023
c33d539
feat(DEV-896): fix graphichs glitches
MikeAtUqido Nov 2, 2023
a6c51d4
feat(DEV-896): update sign up page
MikeAtUqido Nov 8, 2023
fe4f12c
Merge remote-tracking branch 'origin/main' into feature/DEV-896-sign-up
MikeAtUqido Nov 8, 2023
3d5c9de
feat(DEV-896): remove unused type
MikeAtUqido Nov 8, 2023
9063388
feat(DEV-896): update email matcher
MikeAtUqido Nov 8, 2023
9093911
Merge remote-tracking branch 'origin/main' into feature/DEV-896-sign-up
MikeAtUqido Nov 8, 2023
a0dfb3e
feat(DEV-896): add additional fields on sign up
MikeAtUqido Nov 8, 2023
4605744
Merge remote-tracking branch 'origin/main' into feature/DEV-896-sign-up
MikeAtUqido Nov 8, 2023
eb34860
chore: remove typo
MikeAtUqido Nov 8, 2023
9f6067f
feat(DEV-896): save sign up info locally
MikeAtUqido Nov 8, 2023
d1daf0f
Merge remote-tracking branch 'origin/main' into feature/DEV-896-sign-up
MikeAtUqido Nov 8, 2023
9b49c94
feat(DEV-896): remove localStorage use
MikeAtUqido Nov 9, 2023
2a62d9c
Merge remote-tracking branch 'origin/main' into feature/DEV-896-sign-up
MikeAtUqido Nov 9, 2023
8b401bd
feat(DEV-896): update sign up page logic
MikeAtUqido Nov 9, 2023
2c2b9e9
feat(DEV-896): add SignUpUserData type
MikeAtUqido Nov 9, 2023
43f7627
chore: remove unused file
MikeAtUqido Nov 10, 2023
e958c53
Merge branch 'main' into feature/DEV-896-sign-up
datalek Nov 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions apps/nextjs-website/src/__tests__/helpers/auth.helpers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { passwordMatcher } from '../../helpers/auth.helpers';

describe('passwordMatch', () => {
it('returns true if the passwords match', () => {
const validPasswords = ['Password1!', 'P4ssw0rd@!'];
const invalidPasswords = [
'password',
'password1',
'password!',
'password1!',
'Password',
'Password1',
'Password!',
];

validPasswords.forEach((password) => {
expect(passwordMatcher.test(password)).toBe(true);
});

invalidPasswords.forEach((password) => {
expect(passwordMatcher.test(password)).toBe(false);
});
});
});
27 changes: 27 additions & 0 deletions apps/nextjs-website/src/_contents/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export const translations = {
confirmPassword: 'Conferma password',
company: 'Tipologia Ente o Azienda',
role: 'Ruolo',
requiredFieldError: 'Questo campo non può essere vuoto',
emailFieldError: 'Inserisci un indirizzo email valido',
},
pageNotFound: {
overline: '404',
Expand Down Expand Up @@ -329,6 +331,31 @@ export const translations = {
welcomeMessage: 'Ti diamo il benvenuto su PagoPA DevPortal.',
yourAccountIsActive: 'Il tuo account è attivo',
},
signUp: {
createYourAccount: 'Crea il tuo account',
confirmComunications:
"Inviami e-mail relative alle risorse e agli aggiornamenti sui prodotti. Se questa casella è selezionata, PagoPA ti invierà di tanto in tanto delle e-mail utili e pertinenti. Puoi annullare l'iscrizione in qualsiasi momento.",
acceptPolicy:
'Cliccando su “Iscriviti” accetti la nostra informativa sul trattamento dei dati personali per la Privacy Policy.',
alreadyHaveAnAccount: 'Hai già un account?',
whyCreateAccount: 'Perché iscriversi a PagoPA DevPortal',
passwordPolicy:
'Minimo 8 caratteri, almeno un numero, almeno una lettera maiuscola e almeno un carattere speciale',
advantages: [
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
],
},
confirmSignUp: {
confirmSignUp: 'Conferma che sei tu',
description: (email: string) =>
`Abbiamo inviato una e-mail a ${email} Clicca sul bottone contenuto al suo interno per verificarla.`,
didntReceiveEmail:
"Non hai ricevuto l'e-mail? Controlla se nella posta indesiderata oppure",
resendEmail: 'Reinvia e-mail',
wrongEmail: "L'indirizzo email è errato?",
},
},
webinar: {
whyParticipate: 'Perché partecipare?',
Expand Down
163 changes: 163 additions & 0 deletions apps/nextjs-website/src/app/auth/sign-up/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
'use client';
import { translations } from '@/_contents/translations';
import PageNotFound from '@/app/not-found';
import CheckItem from '@/components/molecules/CheckItem/CheckItem';
import ConfirmSignUp from '@/components/organisms/Auth/ConfirmSignUp';
import SignUpForm from '@/components/organisms/Auth/SignUpForm';
import { environment } from '@/config';
import { SignUpSteps } from '@/lib/types/signUpSteps';
import {
Alert,
Box,
Grid,
Snackbar,
Typography,
useMediaQuery,
} from '@mui/material';
import { Auth } from 'aws-amplify';
import { useCallback, useState } from 'react';

const SignUp = () => {
const {
auth: { signUp },
} = translations;

const isSmallScreen = useMediaQuery('(max-width: 1000px)');
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [company, setCompany] = useState('');
const [role, setRole] = useState('');
const [mailinglistAccepted, setMailinglistAccepted] = useState(false);
const [signUpStep, setSignUpStep] = useState(SignUpSteps.SIGN_UP);
const [error, setError] = useState<string | null>(null);

const onSignUp = useCallback(async () => {
const result = await Auth.signUp({
MikeAtUqido marked this conversation as resolved.
Show resolved Hide resolved
datalek marked this conversation as resolved.
Show resolved Hide resolved
username,
password,
attributes: {
given_name: firstName,
family_name: lastName,
'custom:privacy_accepted': 'true',
'custom:mailinglist_accepted': mailinglistAccepted ? 'true' : 'false',
'custom:job_role': role,
'custom:company_type': company,
},
}).catch((error) => {
setError(error.message);
return false;
});

if (typeof result === 'boolean') {
return result;
} else {
setSignUpStep(SignUpSteps.CONFIRM_SIGN_UP);
return !!result.user;
}
}, [
company,
firstName,
lastName,
mailinglistAccepted,
password,
role,
username,
]);

const onBackStep = useCallback(() => {
setSignUpStep(SignUpSteps.SIGN_UP);
return null;
}, []);

const onResendEmail = useCallback(async () => {
await Auth.resendSignUp(username);
return null;
}, [username]);

if (environment === 'prod') {
MikeAtUqido marked this conversation as resolved.
Show resolved Hide resolved
return <PageNotFound />;
}

return (
<>
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundImage: 'url(/images/hero.jpg)',
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
backgroundPosition: 'bottom right',
}}
>
<Grid
container
spacing={isSmallScreen ? 0 : 1}
alignItems={isSmallScreen ? 'center' : 'flex-start'}
justifyContent='center'
direction={isSmallScreen ? 'column-reverse' : 'row'}
mx={isSmallScreen ? 3 : 20}
my={isSmallScreen ? 3 : 15}
>
<Grid item xs={isSmallScreen ? 1 : 5}>
<Typography variant='h6' mb={4} mt={isSmallScreen ? 10 : 0}>
{signUp.whyCreateAccount}
</Typography>
{signUp.advantages.map((advantage, index) => {
return (
<CheckItem
key={index}
title={`Vantaggio ${index}`}
MikeAtUqido marked this conversation as resolved.
Show resolved Hide resolved
description={advantage}
/>
);
})}
</Grid>
<Grid item xs={isSmallScreen ? 1 : 5}>
{signUpStep === SignUpSteps.SIGN_UP && (
<SignUpForm
username={username}
setUsername={setUsername}
password={password}
setPassword={setPassword}
confirmPassword={confirmPassword}
setConfirmPassword={setConfirmPassword}
firstName={firstName}
setFirstName={setFirstName}
lastName={lastName}
setLastName={setLastName}
company={company}
setCompany={setCompany}
role={role}
setRole={setRole}
mailinglistAccepted={mailinglistAccepted}
setMailinglistAccepted={setMailinglistAccepted}
onSignUp={onSignUp}
/>
)}
{signUpStep === SignUpSteps.CONFIRM_SIGN_UP && (
<ConfirmSignUp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I'm on the confirmation page, if I refresh the page and click the "Reinvia e-mail" link I get an error

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a bug with the email aliases (those with a + sign), it doesn't happen with regular emails, should we correct for those email or we want to let users set up multiple accounts with the same email even in production?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MikeAtUqido IMHO it should be fixed. I think the best option, at the moment, is to create an activity explaining the problem and then add the link of the activity here.
can you do it, please?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the activity: DEV-1114

email={username}
onBack={onBackStep}
onResendEmail={onResendEmail}
/>
MikeAtUqido marked this conversation as resolved.
Show resolved Hide resolved
)}
</Grid>
</Grid>
</Box>
<Snackbar
open={!!error}
autoHideDuration={2000}
onClose={() => setError(null)}
>
<Alert severity='error'>{error}</Alert>
</Snackbar>
</>
);
};

export default SignUp;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { SvgIcon } from '@mui/material';

const IconInbox = () => {
return (
<SvgIcon sx={{ width: '80px', height: '64px' }}>
<svg
width='80'
height='64'
viewBox='0 0 80 64'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<g id='Group'>
<g id='Group_2'>
<g id='Group_3'>
<path
id='Vector'
d='M80 15.732C80 7.29859 73.1389 0.4375 64.7055 0.4375C56.3705 0.4375 49.5723 7.13953 49.4147 15.4375H37.9256C37.0628 15.4375 36.3631 16.137 36.3631 17C36.3631 17.863 37.0628 18.5625 37.9256 18.5625H49.6745C50.4091 22.4719 52.6356 25.8608 55.7384 28.1141L37.2677 46.5852C36.7291 47.1239 36.0127 47.4205 35.2509 47.4205C34.4891 47.4205 33.7727 47.1239 33.2341 46.585L5.52141 18.8727C5.99156 18.6731 6.50828 18.5625 7.05031 18.5625H23.8633C24.7261 18.5625 25.4258 17.863 25.4258 17C25.4258 16.137 24.7261 15.4375 23.8633 15.4375H7.05031C3.16266 15.4375 0 18.6003 0 22.4878V56.5122C0 60.3997 3.16266 63.5625 7.05031 63.5625H63.5747C67.4623 63.5625 70.625 60.3997 70.625 56.5122V29.8342C76.1273 27.5158 80 22.0678 80 15.732ZM3.4 57.9528C3.22328 57.5064 3.125 57.0208 3.125 56.5122V22.4878C3.125 22.013 3.20984 21.5575 3.365 21.1358L21.7909 39.5617L3.4 57.9528ZM7.05031 60.4375C6.54188 60.4375 6.05609 60.3392 5.60969 60.1625L24.0006 41.7716L31.0241 48.795C32.1894 49.9603 33.72 50.543 35.2506 50.543C36.7814 50.543 38.312 49.9603 39.4773 48.795L46.5625 41.7098L65.0153 60.1625C64.5689 60.3392 64.0833 60.4375 63.5747 60.4375H7.05031ZM67.5 56.5122C67.5 57.0206 67.4017 57.5064 67.225 57.9527L48.7723 39.5L58.5436 29.7288C60.4297 30.5623 62.5144 31.0266 64.7056 31.0266C65.6598 31.0266 66.5938 30.9378 67.5002 30.7698V56.5122H67.5ZM64.7055 27.9016C57.9952 27.9016 52.5359 22.4423 52.5359 15.732C52.5359 9.02172 57.9952 3.5625 64.7055 3.5625C71.4158 3.5625 76.875 9.02172 76.875 15.732C76.875 22.4423 71.4159 27.9016 64.7055 27.9016Z'
fill='#00C5CA'
/>
<path
id='Vector_2'
d='M65.4868 8.38818H62.8149C61.9521 8.38818 61.2524 9.08771 61.2524 9.95068C61.2524 10.8137 61.9521 11.5132 62.8149 11.5132H63.9243V22.4507C63.9243 23.3137 64.624 24.0132 65.4868 24.0132C66.3496 24.0132 67.0493 23.3137 67.0493 22.4507V9.95068C67.0493 9.08771 66.3498 8.38818 65.4868 8.38818Z'
fill='#00C5CA'
/>
<path
id='Vector_3'
d='M30.8945 18.5625C31.7575 18.5625 32.457 17.8629 32.457 17C32.457 16.1371 31.7575 15.4375 30.8945 15.4375C30.0316 15.4375 29.332 16.1371 29.332 17C29.332 17.8629 30.0316 18.5625 30.8945 18.5625Z'
fill='#00C5CA'
/>
</g>
</g>
</g>
</svg>
</SvgIcon>
);
};

export default IconInbox;
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use client';
import { CheckCircle } from '@mui/icons-material';
import { Grid, Typography, useTheme } from '@mui/material';

interface CheckItemProps {
title: string;
description: string;
}

const CheckItem = ({ title, description }: CheckItemProps) => {
const { palette } = useTheme();

return (
<Grid container alignItems='flex-start' mb={4}>
<Grid item xs={1} mr={3}>
<CheckCircle sx={{ fontSize: 40 }} color='info' />
</Grid>
<Grid item xs={10}>
<Typography
variant='overline'
content='div'
mb={2}
color={palette.text.secondary}
fontSize={14}
>
{title}
</Typography>
<Typography content='div'>{description}</Typography>
</Grid>
</Grid>
);
};

export default CheckItem;
kin0992 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { TextField, TextFieldProps } from '@mui/material';
import { FC, useCallback, useEffect, useState } from 'react';

export type ValidatorFunction = (value: string) => {
valid: boolean;
error: string;
};

type RequiredTextFieldProps = Partial<TextFieldProps> & {
value: string;
customValidators?: ValidatorFunction[];
};

const RequiredTextField: FC<RequiredTextFieldProps> = ({
MikeAtUqido marked this conversation as resolved.
Show resolved Hide resolved
label,
value,
onChange,
helperText,
customValidators,
type = 'text',
...rest
}) => {
const [isDirty, setIsDirty] = useState(false);
const [isValid, setIsValid] = useState(false);
const [errorText, setErrorText] = useState(helperText);

const validateField = useCallback(() => {
if (!value || value?.trim().length === 0) {
setIsValid(false);
setErrorText(helperText);
} else {
setIsValid(true);
}
}, [helperText, value]);

useEffect(() => {
validateField();

if (customValidators) {
const { valid, error } = customValidators.reduce(
(acc, validator) => {
const { valid, error } = validator(value);
return valid ? acc : { valid, error };
},
{ valid: true, error: '' }
);
setIsValid(valid);
setErrorText(error);
}
}, [value, validateField, customValidators]);

return (
<TextField
label={label}
variant='outlined'
size='small'
required
type={type}
value={value}
onChange={onChange}
onBlur={() => setIsDirty(true)}
sx={{
backgroundColor: 'white',
width: '100%',
}}
error={isDirty && !isValid}
helperText={isDirty && !isValid && errorText}
{...rest}
/>
);
};

export default RequiredTextField;
Loading
Loading