diff --git a/zubhub_frontend/zubhub/src/assets/images/cream.png b/zubhub_frontend/zubhub/src/assets/images/cream.png new file mode 100644 index 000000000..5038b635e Binary files /dev/null and b/zubhub_frontend/zubhub/src/assets/images/cream.png differ diff --git a/zubhub_frontend/zubhub/src/assets/images/space.png b/zubhub_frontend/zubhub/src/assets/images/space.png new file mode 100644 index 000000000..4eac05783 Binary files /dev/null and b/zubhub_frontend/zubhub/src/assets/images/space.png differ diff --git a/zubhub_frontend/zubhub/src/assets/js/styles/components/button/buttonStyles.js b/zubhub_frontend/zubhub/src/assets/js/styles/components/button/buttonStyles.js index 9767cc299..d95d040ac 100644 --- a/zubhub_frontend/zubhub/src/assets/js/styles/components/button/buttonStyles.js +++ b/zubhub_frontend/zubhub/src/assets/js/styles/components/button/buttonStyles.js @@ -3,8 +3,12 @@ import { colors } from "../../../colors"; const styles = theme => ({ primaryButtonStyle: { backgroundColor: 'var(--primary-color3)', - borderRadius: 30, + borderRadius: theme.spacing(1), color: 'white', + '&.disabled': { + color: '#7BA8AB', + background: 'rgba(0, 184, 196, 0.10)', + }, '&:hover': { backgroundColor: 'var(--secondary-color6)', }, diff --git a/zubhub_frontend/zubhub/src/assets/js/styles/views/login/loginStyles.js b/zubhub_frontend/zubhub/src/assets/js/styles/views/login/loginStyles.js index 8e951117b..b0b74f85e 100644 --- a/zubhub_frontend/zubhub/src/assets/js/styles/views/login/loginStyles.js +++ b/zubhub_frontend/zubhub/src/assets/js/styles/views/login/loginStyles.js @@ -1,127 +1,188 @@ -import { fade } from '@mui/material/styles'; - -const styles = theme => ({ - root: { - paddingTop: '2em', - paddingBottom: '2em', - flex: '1 0 auto', - // background: 'var(--primary-color2)', - // background: - // 'linear-gradient(to bottom, var(--primary-color2) 0%, var(--primary-color2) 25%, rgba(255,255,255,1) 61%, rgba(255,255,255,1) 100%)', - }, - containerStyle: { - maxWidth: '600px', - [theme.breakpoints.up('1600')]: { - maxWidth: '950px', +export const mainStyles = theme => ({ + wrapper: { + marginTop: theme.spacing(10), + [theme.breakpoints.down('lg')]: { + marginTop: theme.spacing(10), }, - [theme.breakpoints.down('400')]: { - marginTop:'30px', + [theme.breakpoints.down('md')]: { + marginTop: theme.spacing(5), }, }, - cardStyle: { - border: 0, - borderRadius: 15, - boxShadow: '0 3px 5px 2px rgba(0, 0, 0, .12)', - color: 'white', - padding: '0 30px', - }, - titleStyle: { - fontWeight: 'bold', - fontSize: '1.7rem', - [theme.breakpoints.up('1600')]: { - fontSize: '2.5rem', - }, + gridContainer: { + alignItems: 'center', + borderRadius: theme.spacing(1), }, - descStyle: { - [theme.breakpoints.up('1600')]: { - fontSize: '1.7rem', + header: { + color: theme.palette.text.primary, + fontSize: theme.spacing(5), + fontWeight: 800, + lineHeight: '50px', + [theme.breakpoints.down('sm')]: { + lineHeight: 'unset', + fontSize: '15px', }, }, - customLabelStyle: { - '&.MuiFormLabel-root.Mui-focused': { - color: 'var(--primary-color3)', + subHeader: { + color: theme.palette.text.primary, + fontSize: theme.spacing(3), + fontWeight: 800, + lineHeight: 'normal', + [theme.breakpoints.down('sm')]: { + fontSize: '18px', + fontWeight: 700, }, - [theme.breakpoints.up('1600')]: { - fontSize: '1.7rem', + }, + text: { + fontSize: theme.spacing(2), + fontWeight: 400, + lineHeight: '32px', + color: theme.palette.text.secondary, + [theme.breakpoints.down('sm')]: { + fontSize: '12px', + lineHeight: 'unset', }, }, - customInputStyle: { - borderRadius: 15, - '&.MuiOutlinedInput-notchedOutline': { - border: '2px solid var(--primary-color3)', - boxShadow: `rgba(var(--primary-color3), 0.2rem) 0 0 0 0.2rem`, + header2: { + color: theme.palette.text.primary, + fontSize: '30px', + fontWeight: 700, + lineHeight: '50px', + }, + subHeader2: { + color: theme.palette.text.primary, + fontSize: '18px', + fontWeight: 600, + lineHeight: '24px', + }, + text2: { + marginBottom: '40px' + }, + wizard: { + width: '100%', + }, + outlinedInput: { + borderRadius: theme.spacing(1), + paddingInline: theme.spacing(3), + gap: theme.spacing(3), + '& .MuiOutlinedInput-input': { + padding: '20px 0', }, '&.MuiOutlinedInput-root': { '&:hover fieldset': { - border: '2px solid var(--primary-color3)', - boxShadow: `rgba(var(--primary-color3), 0.2rem) 0 0 0 0.2rem`, + border: `1px solid ${theme.palette.primary.light}`, }, '&.Mui-focused fieldset': { - border: '2px solid var(--primary-color3)', - boxShadow: `rgba(var(--primary-color3), 0.2rem) 0 0 0 0.2rem`, - }, - [theme.breakpoints.up('1600')]: { - fontSize: '1.7rem', + border: `2px solid ${theme.palette.primary.light}`, }, }, }, - secondaryLink: { - color: 'var(--primary-color3)', - '&:hover': { - color: 'var(--secondary-color6)', + formControl: { + gap: theme.spacing(3), + [theme.breakpoints.down('md')]: { + gap: theme.spacing(2), }, - [theme.breakpoints.up('1600')]: { - fontSize: '1.2rem', + [theme.breakpoints.down('sm')]: { + gap: theme.spacing(1), }, }, - center: { - display: 'flex', - justifyContent: 'center', - alignItems: 'center', + inputIcon: { + fontSize: theme.spacing(3), + fill: theme.palette.grey[400], }, - dividerText: { - whiteSpace: 'nowrap', - [theme.breakpoints.up('1600')]: { - fontSize: '1.2rem', + button: { + fontSize: '18px', + fontWeight: 600, + paddingBlock: '20px', + [theme.breakpoints.down('sm')]: { + fontSize: '12px', + padding: '5px 16px', }, }, - divider: { - width: '30%', - marginRight: '1em', - marginLeft: '1em', - [theme.breakpoints.up('1600')]: { - height: '0.1em', + spaceBackground: { + width: '100%', + height: '100%', + }, + backContainer: { + borderRadius: '50%', + background: theme.palette.background.default, + padding: '10px', + }, + backIcon: { + fontSize: theme.spacing(3), + }, + gridControl: { + background: theme.palette.background.default, + padding: '0 40px', + boxShadow: '2px 4px 5px 0px rgba(0, 184, 196, 0.05)', + }, +}); + +export const step1Styles = theme => ({ + paper: { + cursor: 'pointer', + width: '350px', + height: '300px', + borderRadius: theme.spacing(1), + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + gap: theme.spacing(2), + justifyContent: 'center', + [theme.breakpoints.down('sm')]: { + padding: theme.spacing(2, 3), + width: '342px', + height: '124px', + alignItems: 'unset', }, - [theme.breakpoints.down('510')]: { - width: '20%', + '&.selected': { + border: '3px solid var(--primary-color3)', }, - [theme.breakpoints.down('381')]: { - marginLeft: '0.5em', - marginRight: '0.5em', + }, + icon: { + fill: '#00B8C4', + fontSize: '64px', + [theme.breakpoints.down('sm')]: { + fontSize: 'unset', + height: '32px', + width: '32px', }, }, - textDecorationNone: { - textDecoration: 'none', +}); + +export const step2Styles = theme => ({ + container: { + background: theme.palette.background.paper, + borderRadius: theme.spacing(1), + padding: theme.spacing(8, 5), + boxShadow: '2px 4px 5px 0px rgba(0, 184, 196, 0.05)', }, - errorBox: { - width: '100%', - padding: '1em', - borderRadius: 6, - borderWidth: '1px', - borderColor: 'var(--primary-color2)', - backgroundColor: 'var(--secondary-color1)', - [theme.breakpoints.up('1600')]: { - fontSize: '1.5rem', - }, +}); + +export const step3Styles = theme => ({}); + +export const step4Styles = theme => ({}); +export const step5Styles = theme => ({ + label: { + gap: theme.spacing(3), }, - error: { - color: 'var(--primary-color2)', + formControl: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(5), + alignItems: 'left', }, - fieldHelperTextStyle: { - [theme.breakpoints.up('1600')]: { - fontSize: '1.2rem', + fieldSet: { + padding: `20px ${theme.spacing(3)}`, + borderWidth: '1px', + borderColor: theme.palette.grey[300], + borderStyle: 'solid', + borderRadius: theme.spacing(1), + '&:hover': { + borderColor: theme.palette.primary.light, + }, + '&.selected': { + borderWidth: '3px', + borderColor: theme.palette.primary.light, }, }, }); - -export default styles; diff --git a/zubhub_frontend/zubhub/src/components/Navbar/Navbar.jsx b/zubhub_frontend/zubhub/src/components/Navbar/Navbar.jsx index 9be26b6be..2b0b535e3 100644 --- a/zubhub_frontend/zubhub/src/components/Navbar/Navbar.jsx +++ b/zubhub_frontend/zubhub/src/components/Navbar/Navbar.jsx @@ -76,6 +76,8 @@ function NavBar(props) { const [searchType, setSearchType] = useState(getQueryParams(window.location.href).get('type') || SearchType.PROJECTS); const formRef = useRef(); const token = useSelector(state => state.auth.token); + const pathname = props.location?.pathname + const hideSearchAndActions = pathname === '/signup' || pathname === '/login'; const [state, setState] = React.useState({ username: null, @@ -215,11 +217,14 @@ function NavBar(props) { - - - - - + {!hideSearchAndActions && ( + + + + + + )} + logo @@ -268,6 +273,8 @@ function NavBar(props) { ))} + { + !hideSearchAndActions && (
+ ) + } -
- - - - - {props.auth.username && ( - - - {props.auth.username} - - {/* Todo: Change this subheading based on current role of user */} - Creator - - )} - - + {!hideSearchAndActions && ( +
+ + + + + {props.auth.username && ( + + + {props.auth.username} + + {/* Todo: Change this subheading based on current role of user */} + Creator + + )} +
+ )} {open_search_form ? ( handleSetState(closeSearchFormOrIgnore(e))}> @@ -455,4 +465,4 @@ const mapDispatchToProps = dispatch => { }; }; -export default connect(mapStateToProps, mapDispatchToProps)(NavBar); +export default connect(mapStateToProps, mapDispatchToProps)(NavBar); \ No newline at end of file diff --git a/zubhub_frontend/zubhub/src/components/form/errorMessage/ErrorMessage.jsx b/zubhub_frontend/zubhub/src/components/form/errorMessage/ErrorMessage.jsx new file mode 100644 index 000000000..5027230e3 --- /dev/null +++ b/zubhub_frontend/zubhub/src/components/form/errorMessage/ErrorMessage.jsx @@ -0,0 +1,9 @@ +import { FormHelperText } from '@mui/material'; +import React from 'react'; + +const CustomErrorMessage = props => { + const { name, touched, errors } = props; + return {touched[name] && errors[name] && <>{errors[name]}}; +}; + +export default CustomErrorMessage; diff --git a/zubhub_frontend/zubhub/src/components/index.js b/zubhub_frontend/zubhub/src/components/index.js index 82af1f05c..08712750c 100644 --- a/zubhub_frontend/zubhub/src/components/index.js +++ b/zubhub_frontend/zubhub/src/components/index.js @@ -15,16 +15,27 @@ import Modal from "./modals/Modal"; import PreviewActivity from "./previewActivity/PreviewActivity"; import PreviewProject from "./previewProject/PreviewProject"; import Pill from "./pill/Pill"; +import CustomErrorMessage from './form/errorMessage/ErrorMessage'; import ProtectedRoute from './protected_route/ProtectedRoute'; export { - Comments, - CustomButton, Dropdown, - Editor, Gallery, ImageInput, - LabeledLine, Modal, OrDivider, PreviewProject, - SelectFromPills, Sidenav, TagsInput, VideoInput, - Collapsible, - PreviewActivity, - Pill, - ProtectedRoute, + Comments, + CustomButton, + Dropdown, + Editor, + Gallery, + ImageInput, + LabeledLine, + Modal, + OrDivider, + PreviewProject, + SelectFromPills, + Sidenav, + TagsInput, + VideoInput, + Collapsible, + PreviewActivity, + Pill, + ProtectedRoute, + CustomErrorMessage, }; diff --git a/zubhub_frontend/zubhub/src/views/login/Login.jsx b/zubhub_frontend/zubhub/src/views/login/Login.jsx index 4cd0500aa..b0e1fa9cd 100644 --- a/zubhub_frontend/zubhub/src/views/login/Login.jsx +++ b/zubhub_frontend/zubhub/src/views/login/Login.jsx @@ -1,273 +1,148 @@ -import React from 'react'; -import clsx from 'clsx'; -import { Link } from 'react-router-dom'; import PropTypes from 'prop-types'; - import { connect } from 'react-redux'; - -import { makeStyles } from '@mui/styles'; -import Visibility from '@mui/icons-material/Visibility'; -import VisibilityOff from '@mui/icons-material/VisibilityOff'; +import * as AuthActions from '../../store/actions/authActions'; import { - Grid, Box, - Divider, - Container, - Card, - CardActionArea, - CardContent, - Typography, + Grid, + FormControl, + FormLabel, + OutlinedInput, InputAdornment, + Typography, IconButton, - OutlinedInput, - InputLabel, - FormHelperText, - FormControl, + FormControlLabel, + Checkbox } from '@mui/material'; +import { CustomButton, CustomErrorMessage } from '../../components'; +import { TfiLock, TfiArrowLeft } from 'react-icons/tfi'; +import { SlEye, SlUser } from 'react-icons/sl'; +import { BsEyeSlash } from 'react-icons/bs'; +import { makeStyles } from '@mui/styles'; +import { useState } from 'react'; +import { mainStyles, step2Styles } from '../../assets/js/styles/views/login/loginStyles'; +import { handleLogin, validationSchema } from './loginScripts'; +import { useFormik } from 'formik'; -import { - validationSchema, - handleClickShowPassword, - handleMouseDownPassword, - login, -} from './loginScripts'; - -import { withFormik } from 'formik'; - -import CustomButton from '../../components/button/Button'; -import * as AuthActions from '../../store/actions/authActions'; -import styles from '../../assets/js/styles/views/login/loginStyles'; -const useStyles = makeStyles(styles); +const useMainStyles = makeStyles(mainStyles); +const useStyles = makeStyles(step2Styles); -/** - * @function Login View - * @author Raymond Ndibe - * - * @todo - describe function's signature - */ -function Login(props) { +const Login = props => { + const [{ password }, setShowPassword] = useState({ password: false }); const classes = useStyles(); - - const [state, setState] = React.useState({ - show_password: false, - }); - - const handleSetState = obj => { - if (obj) { - Promise.resolve(obj).then(obj => { - setState(state => ({ ...state, ...obj })); - }); - } - }; - - const { show_password } = state; - const { t } = props; + const mainClasses = useMainStyles(); + const { + errors, + handleChange, + handleBlur, + handleSubmit, + touched, + } = useFormik({ + initialValues: { + username: '', + password: '', + remember: false + }, + validationSchema: validationSchema, + onSubmit: async (values) => await handleLogin(props, values) + }) return ( - - - - - -
handleSetState(login(e, props))} - > - - {t('login.welcomeMsg.primary')} - - - {t('login.welcomeMsg.secondary')} - - - - - {props.status && props.status['non_field_errors'] && ( - - {props.status['non_field_errors']} - - )} - - - - - - {t('login.inputs.username.label')} - - - - {(props.status && props.status['username']) || - (props.touched['username'] && - props.errors['username'] && - t( - `login.inputs.username.errors.${props.errors['username']}`, - ))} - - - - - - - - {t('login.inputs.password.label')} - - - - handleSetState(handleClickShowPassword(state)) - } - onMouseDown={handleMouseDownPassword} - edge="end" - > - {show_password ? ( - - ) : ( - - )} - - - } - label={t('login.inputs.password.label')} - /> - - {(props.status && props.status['password']) || - (props.touched['password'] && - props.errors['password'] && - t( - `login.inputs.password.errors.${props.errors['password']}`, - ))} - - - - - - {t('login.inputs.submit')} - - - -
- - - - - - {t('login.notAMember')} - - - - - - - - {t('login.signup')} - - - - - - - {t('login.forgotPassword')} - - - - -
-
-
-
+ + +
+ + + Welcome to ZubHub ! + + + Log Into your ZubHub account to share ideas + + + + + Username or Email address + + + + } + /> + + + + + + Password + + + + } + endAdornment={ + + setShowPassword({ password: !password })}> + {password ? ( + + ) : ( + + )} + + + } + /> + + + + + + } + /> + + + + + Login + + +
+
); -} +}; Login.propTypes = { auth: PropTypes.object.isRequired, @@ -295,12 +170,4 @@ const mapDispatchToProps = dispatch => { export default connect( mapStateToProps, mapDispatchToProps, -)( - withFormik({ - mapPropsToValue: () => ({ - username: '', - password: '', - }), - validationSchema, - })(Login), -); +)(Login); diff --git a/zubhub_frontend/zubhub/src/views/login/loginScripts.js b/zubhub_frontend/zubhub/src/views/login/loginScripts.js index 61084b1a6..722674e31 100644 --- a/zubhub_frontend/zubhub/src/views/login/loginScripts.js +++ b/zubhub_frontend/zubhub/src/views/login/loginScripts.js @@ -27,11 +27,9 @@ export const handleMouseDownPassword = e => { * * @todo - describe function's signature */ -export const login = (e, props) => { - e.preventDefault(); - props.setFieldTouched('username', true); +export const handleLogin = (props, values) => { return props - .login({ values: props.values, navigate: props.navigate }) + .login({ values, navigate: props.navigate }) .catch(error => { const messages = JSON.parse(error.message); if (typeof messages === 'object') { @@ -61,4 +59,5 @@ export const login = (e, props) => { export const validationSchema = Yup.object().shape({ username: Yup.string().required('required'), password: Yup.string().min(8, 'min').required('required'), + remember: Yup.boolean() }); diff --git a/zubhub_frontend/zubhub/src/views/signup/EducatorsForm.jsx b/zubhub_frontend/zubhub/src/views/signup/EducatorsForm.jsx new file mode 100644 index 000000000..0a0b9c691 --- /dev/null +++ b/zubhub_frontend/zubhub/src/views/signup/EducatorsForm.jsx @@ -0,0 +1,284 @@ +import { + Box, + Grid, + FormControl, + FormLabel, + OutlinedInput, + InputAdornment, + Typography, + MenuItem, + Select, + FormControlLabel, + Checkbox, + IconButton, +} from '@mui/material'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import { CustomErrorMessage, CustomButton } from '../../components'; +import { SlEnvolope, SlUser, SlEye } from 'react-icons/sl'; +import { BsEyeSlash } from 'react-icons/bs'; +import { TfiLocationPin, TfiLock, TfiAngleDown, TfiControlRecord } from 'react-icons/tfi'; +import { makeStyles } from '@mui/styles'; +import { mainStyles } from './signupStyles'; +import dayjs from 'dayjs'; +import 'dayjs/locale/de'; +import { useState } from 'react'; + +const useMainStyles = makeStyles(mainStyles); + +const EducatorsForm = props => { + const [{ password, confirmPassword }, setShowPassword] = useState({ password: false, confirmPassword: false }); + const mainClasses = useMainStyles(); + const { + setFieldValue, + errors, + handleChange, + handleBlur, + handleSubmit, + touched, + values: { dateOfBirth }, + } = props; + + const getMaxDate = () => { + const currentDate = new Date(); + const maxDate = new Date(); + maxDate.setFullYear(currentDate.getFullYear() - 12); + return dayjs(maxDate); + }; + + return ( + + + + + Welcome to ZubHub ! + + + Create projects, share ideas, make friends. It’s free{' '} + + + + + Username + + + + } + /> + + + + + + Email Address + + + + } + /> + + + + + + When were you born? + + setFieldValue('dateOfBirth', date)} + onBlur={handleBlur} + slotProps={{ + inputAdornment: { position: 'start' }, + textField: { + placeholder: 'dd/mm/yyyy', + }, + }} + /> + + + + + + + Where do you live? + + + + + + + What's your gender? + + + + + + + Password + + + + } + endAdornment={ + + setShowPassword({ password: !password })}> + {password ? ( + + ) : ( + + )} + + + } + /> + + + + + + Confirm Password + + + + } + endAdornment={ + + setShowPassword({ confirmPassword: !confirmPassword })}> + {confirmPassword ? ( + + ) : ( + + )} + + + } + /> + + + + + + } + /> + + + + + Create Your Account + + + + By signing up you agree to our Terms and Services and Privacy Policy + + + + ); +}; + +export default EducatorsForm; diff --git a/zubhub_frontend/zubhub/src/views/signup/Signup.jsx b/zubhub_frontend/zubhub/src/views/signup/Signup.jsx index 492f8e15c..45fd32636 100644 --- a/zubhub_frontend/zubhub/src/views/signup/Signup.jsx +++ b/zubhub_frontend/zubhub/src/views/signup/Signup.jsx @@ -1,678 +1,79 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; -import clsx from 'clsx'; +import StepWizard from 'react-step-wizard'; import PropTypes from 'prop-types'; - +import * as AuthActions from '../../store/actions/authActions'; import { connect } from 'react-redux'; - -import { withFormik } from 'formik'; - -import 'intl-tel-input/build/css/intlTelInput.css'; - +import { Step1, Step2, Step3, Step4, Step5, Step6 } from './steps'; +import { useRef, useState, useEffect } from 'react'; +import { useFormik } from 'formik'; +import { Container, Grid, Typography } from '@mui/material'; import { makeStyles } from '@mui/styles'; -import Visibility from '@mui/icons-material/Visibility'; -import VisibilityOff from '@mui/icons-material/VisibilityOff'; -import { - Grid, - Box, - Divider, - Container, - Card, - CardActionArea, - CardContent, - Select, - MenuItem, - Typography, - InputAdornment, - IconButton, - OutlinedInput, - Tooltip, - ClickAwayListener, - InputLabel, - FormHelperText, - FormControl, - FormControlLabel, - Checkbox, -} from '@mui/material'; - -import { - vars, - validationSchema, - initIntlTelInput, - handleMouseDownPassword, - getLocations, - signup, - handleTooltipOpen, - handleTooltipClose, - handleToggleSubscribeBox, - handleLocationChange, - setLabelWidthOfStaticFields, -} from './signupScripts'; - -import CustomButton from '../../components/button/Button'; -import * as AuthActions from '../../store/actions/authActions'; -import styles from '../../assets/js/styles/views/signup/signupStyles'; - -const useStyles = makeStyles(styles); +import { customValidation, formikSchema } from './signupScripts'; +import { mainStyles } from './signupStyles'; +import { Link } from 'react-router-dom'; -/** - * @function Signup View - * @author Raymond Ndibe - * - * @todo - describe function's signature - */ +const useStyles = makeStyles(mainStyles); function Signup(props) { - const classes = useStyles(); - - const refs = { - phone_el: React.useRef(null), - location_el: React.useRef(null), - date_of_birth_el: React.useRef(null), - }; - - const [state, setState] = React.useState({ - locations: [], - show_password1: false, - show_password2: false, - tool_tip_open: false, - subscribe_box_checked: false, + const { getLocations } = props; + const [locations, setLocations] = useState([]); + const mainClasses = useStyles(); + + const formik = useFormik({ + ...formikSchema, + validate: values => { + return customValidation(values, props); + }, + onSubmit: (values, formikHelpers) => { + console.log(values, formikHelpers, props); + }, }); + const wizardRef = useRef(null); + const [activeStep, setActiveStep] = useState(1); - React.useEffect(() => { - handleSetState(getLocations(props)); - }, []); - - React.useEffect(() => { - initIntlTelInput(props, refs); - }, [refs.phone_el]); - - React.useEffect(() => { - setLabelWidthOfStaticFields(refs, document, props); - }, [props.i18n.language]); - - React.useEffect(() => { - if (props.touched['email']) { - vars.email_field_touched = true; - } else { - vars.email_field_touched = false; + const wizardGo = (direction, stepErrorsAvailable) => { + if (stepErrorsAvailable) { + return; } - - if (props.touched['phone']) { - vars.phone_field_touched = true; - } else { - vars.phone_field_touched = false; + if (direction === 'next') { + wizardRef.current.nextStep(); + setActiveStep(step => step + 1); } - }, [props.touched['email'], props.touched['phone']]); - - const handleSetState = obj => { - if (obj) { - Promise.resolve(obj).then(obj => { - setState(state => ({ ...state, ...obj })); - }); + if (direction === 'prev') { + wizardRef.current.previousStep(); + setActiveStep(step => step - 1); } }; - const { - locations, - tool_tip_open, - show_password1, - show_password2, - subscribe_box_checked, - } = state; - const { t } = props; - - return ( - - - - - -
signup(e, props)} - > - - {t('signup.welcomeMsg.primary')} - - - {t('signup.welcomeMsg.secondary')} - - - - - {props.status && props.status['non_field_errors'] && ( - - {props.status['non_field_errors']} - - )} - - - - - - {t('signup.inputs.username.label')} - - handleSetState(handleTooltipClose())} - > - handleSetState(handleTooltipClose())} - PopperProps={{ - disablePortal: true, - }} - open={tool_tip_open} - disableFocusListener - disableHoverListener - disableTouchListener - > - handleSetState(handleTooltipOpen())} - onChange={props.handleChange} - onBlur={props.handleBlur} - label={t('signup.inputs.username.label')} - /> - - - - {(props.status && props.status['username']) || - (props.touched['username'] && - props.errors['username'] && - t( - `signup.inputs.username.errors.${props.errors['username']}`, - ))} - - - - - - - - {t('signup.inputs.location.label')} - - - - {(props.status && props.status['user_location']) || - (props.touched['user_location'] && - props.errors['user_location'] && - t( - `signup.inputs.location.errors.${props.errors['user_location']}`, - ))} - - - + useEffect(() => { + const response = getLocations({ ...props }); - - - - {t('signup.inputs.dateOfBirth.label')} - - - - {(props.status && props.status['dateOfBirth']) || - (props.touched['dateOfBirth'] && - props.errors['dateOfBirth'] && - t( - `signup.inputs.dateOfBirth.errors.${props.errors['dateOfBirth']}`, - ))} - - - - - - - - {t('signup.inputs.phone.label')} - - - - {(props.status && props.status['phone']) || - (props.touched['phone'] && - props.errors['phone'] && - t( - `signup.inputs.phone.errors.${props.errors['phone']}`, - ))} - - - - - - - - {t('signup.inputs.email.label')} - - - - {(props.status && props.status['email']) || - (props.touched['email'] && - props.errors['email'] && - t( - `signup.inputs.email.errors.${props.errors['email']}`, - ))} - - - - - - - - {t('signup.inputs.password1.label')} - - - - setState({ - ...state, - show_password1: !show_password1, - }) - } - onMouseDown={handleMouseDownPassword} - edge="end" - > - {show_password1 ? ( - - ) : ( - - )} - - - } - label={t('signup.inputs.password1.label')} - /> - - {(props.status && props.status['password1']) || - (props.touched['password1'] && - props.errors['password1'] && - t( - `signup.inputs.password1.errors.${props.errors['password1']}`, - ))} - - - - - - - - {t('signup.inputs.password2.label')} - - - - setState({ - ...state, - show_password2: !show_password2, - }) - } - onMouseDown={handleMouseDownPassword} - edge="end" - > - {show_password2 ? ( - - ) : ( - - )} - - - } - label={t('signup.inputs.password2.label')} - /> - - {(props.status && props.status['password2']) || - (props.touched['password2'] && - props.errors['password2'] && - t( - `signup.inputs.password2.errors.${props.errors['password2']}`, - ))} - - - - - - - - {t('signup.inputs.bio.label')} - - - - - {t('signup.inputs.bio.helpText')} - -
- {(props.status && props.status['bio']) || - (props.touched['bio'] && - props.errors['bio'] && - t( - `signup.inputs.bio.errors.${props.errors['bio']}`, - ))} -
-
-
- - - handleSetState( - handleToggleSubscribeBox(e, props, state), - ) - } - control={ - - } - label={ - - {t('signup.unsubscribe')} - - } - labelPlacement="end" - /> - + if (response) { + Promise.resolve(response).then(data => setLocations(data)); + } + }, [getLocations]); - - - {t('signup.inputs.submit')} - - -
-
- - - - - - {t('signup.alreadyAMember')} - - - - - - - - {t('signup.login')} - - - - -
-
-
-
-
+ return ( + + + + + + + + + + + + + + + Already have an account?{' '} + + Login{' '} + + + + + ); } @@ -703,20 +104,4 @@ const mapDispatchToProps = dispatch => { }; }; -export default connect( - mapStateToProps, - mapDispatchToProps, -)( - withFormik({ - mapPropsToValue: () => ({ - username: '', - email: '', - phone: '', - user_location: '', - password1: '', - password2: '', - bio: '', - }), - validationSchema, - })(Signup), -); +export default connect(mapStateToProps, mapDispatchToProps)(Signup); diff --git a/zubhub_frontend/zubhub/src/views/signup/signupScripts.js b/zubhub_frontend/zubhub/src/views/signup/signupScripts.js index f042d4530..b54899fd6 100644 --- a/zubhub_frontend/zubhub/src/views/signup/signupScripts.js +++ b/zubhub_frontend/zubhub/src/views/signup/signupScripts.js @@ -214,32 +214,67 @@ export const setLabelWidthOfStaticFields = (refs, document, props) => { * @todo - describe object's function */ export const validationSchema = Yup.object().shape({ + role: Yup.string().required('Please choose a role'), username: Yup.string().required('required'), - email: Yup.string() - .email('invalid') - .test('email_is_empty', 'phoneOrEmail', function (value) { - return vars.email_field_touched && !value && !this.parent.phone - ? false - : true; - }), - phone: Yup.string() - .test('phone_is_invalid', 'invalid', function () { - return vars.iti.isValidNumber() || !vars.iti.getNumber() ? true : false; - }) - .test('phone_is_empty', 'phoneOrEmail', function () { - return vars.phone_field_touched && - !vars.iti.getNumber() && - !this.parent.email - ? false - : true; - }), - dateOfBirth: Yup.date().max(new Date(new Date().getFullYear() - 12, 1, 1), 'oldEnough').required('required'), - user_location: Yup.string().min(1, 'min').required('required'), - password1: Yup.string().min(8, 'min').required('required'), - password2: Yup.string() - .oneOf([Yup.ref('password1'), null], 'noMatch') + email: Yup.string().email('invalid').required('Add your email'), + // .test('email_is_empty', 'phoneOrEmail', function (value) { + // return vars.email_field_touched && !value && !this.parent.phone ? false : true; + // }), + phone: Yup.string(), + // .test('phone_is_invalid', 'invalid', function () { + // return vars.iti.isValidNumber() || !vars.iti.getNumber() ? true : false; + // }) + // .test('phone_is_empty', 'phoneOrEmail', function () { + // return vars.phone_field_touched && !vars.iti.getNumber() && !this.parent.email ? false : true; + // }), + dateOfBirth: Yup.date().required('required'), + location: Yup.string().min(1, 'min').required('required'), + password: Yup.string().min(8, 'min').required('required'), + confirmPassword: Yup.string() + .oneOf([Yup.ref('confirmPassword'), null], 'noMatch') .required('required'), bio: Yup.string().max(255, 'tooLong'), + parentEmail: Yup.string().email(), + parentPhone: Yup.string(), + gender: Yup.string().required('Choose your gender'), + sendTips: Yup.boolean(), }); // /^[+][0-9]{9,15}$/g.test(value) + +export const formikSchema = { + initialValues: { + dateOfBirth: '', + role: '', + username: '', + email: '', + phone: '', + location: '', + password: '', + confirmPassword: '', + bio: '', + parentEmail: '', + parentPhone: '', + gender: null, + sendTips: false, + }, + validationSchema, +}; + +export const customValidation = (values, props) => { + const errors = {}; + const { role, parentEmail, parentPhone } = values; + + if (role === 'creator') { + errors.email = null; + + if (!parentEmail && !parentPhone) { + errors.parentEmail = 'Please provide either parent email or phone number'; + errors.parentPhone = 'Please provide either parent email or phone number'; + } else { + errors.parentEmail = null; + errors.parentPhone = null; + } + } + return errors; +}; diff --git a/zubhub_frontend/zubhub/src/views/signup/signupStyles.js b/zubhub_frontend/zubhub/src/views/signup/signupStyles.js new file mode 100644 index 000000000..df26829e6 --- /dev/null +++ b/zubhub_frontend/zubhub/src/views/signup/signupStyles.js @@ -0,0 +1,186 @@ +export const mainStyles = theme => ({ + wrapper: { + marginTop: theme.spacing(10), + [theme.breakpoints.down('lg')]: { + marginTop: theme.spacing(10), + }, + [theme.breakpoints.down('md')]: { + marginTop: theme.spacing(5), + }, + }, + gridContainer: { + alignItems: 'center', + borderRadius: theme.spacing(1), + }, + header: { + color: theme.palette.text.primary, + fontSize: theme.spacing(5), + fontWeight: 800, + lineHeight: '50px', + [theme.breakpoints.down('sm')]: { + lineHeight: 'unset', + fontSize: '20px', + }, + }, + subHeader: { + color: theme.palette.text.primary, + fontSize: theme.spacing(3), + fontWeight: 800, + lineHeight: 'normal', + [theme.breakpoints.down('sm')]: { + fontSize: '18px', + fontWeight: 700, + }, + }, + text: { + fontSize: theme.spacing(2), + fontWeight: 400, + lineHeight: '32px', + color: theme.palette.text.secondary, + [theme.breakpoints.down('sm')]: { + fontSize: '12px', + lineHeight: 'unset', + }, + }, + header2: { + color: theme.palette.text.primary, + fontSize: '30px', + fontWeight: 700, + lineHeight: '50px', + }, + subHeader2: { + color: theme.palette.text.primary, + fontSize: '18px', + fontWeight: 600, + lineHeight: '24px', + }, + text2: {}, + wizard: { + width: '100%', + }, + outlinedInput: { + borderRadius: theme.spacing(1), + paddingInline: theme.spacing(3), + gap: theme.spacing(3), + '& .MuiOutlinedInput-input': { + padding: '20px 0', + }, + '&.MuiOutlinedInput-root': { + '&:hover fieldset': { + border: `1px solid ${theme.palette.primary.light}`, + }, + '&.Mui-focused fieldset': { + border: `2px solid ${theme.palette.primary.light}`, + }, + }, + }, + formControl: { + gap: theme.spacing(3), + [theme.breakpoints.down('md')]: { + gap: theme.spacing(2), + }, + [theme.breakpoints.down('sm')]: { + gap: theme.spacing(1), + }, + }, + inputIcon: { + fontSize: theme.spacing(3), + fill: theme.palette.grey[400], + }, + button: { + fontSize: '18px', + fontWeight: 600, + paddingBlock: '20px', + [theme.breakpoints.down('sm')]: { + fontSize: '12px', + padding: '5px 16px', + }, + }, + spaceBackground: { + width: '100%', + height: '100%', + }, + backContainer: { + borderRadius: '50%', + background: theme.palette.background.default, + padding: '10px', + }, + backIcon: { + fontSize: theme.spacing(3), + }, + gridControl: { + background: theme.palette.background.default, + padding: '0 40px', + boxShadow: '2px 4px 5px 0px rgba(0, 184, 196, 0.05)', + }, +}); + +export const step1Styles = theme => ({ + paper: { + cursor: 'pointer', + width: '350px', + height: '300px', + borderRadius: theme.spacing(1), + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + gap: theme.spacing(2), + justifyContent: 'center', + [theme.breakpoints.down('sm')]: { + padding: theme.spacing(2, 3), + width: '342px', + height: '124px', + alignItems: 'unset', + }, + '&.selected': { + border: '3px solid var(--primary-color3)', + }, + }, + icon: { + fill: '#00B8C4', + fontSize: '64px', + [theme.breakpoints.down('sm')]: { + fontSize: 'unset', + height: '32px', + width: '32px', + }, + }, +}); + +export const step2Styles = theme => ({ + container: { + background: theme.palette.background.paper, + borderRadius: theme.spacing(1), + padding: theme.spacing(8, 5), + boxShadow: '2px 4px 5px 0px rgba(0, 184, 196, 0.05)', + }, +}); + +export const step3Styles = theme => ({}); + +export const step4Styles = theme => ({}); +export const step5Styles = theme => ({ + label: { + gap: theme.spacing(3), + }, + formControl: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(5), + alignItems: 'left', + }, + fieldSet: { + padding: `20px ${theme.spacing(3)}`, + borderWidth: '1px', + borderColor: theme.palette.grey[300], + borderStyle: 'solid', + borderRadius: theme.spacing(1), + '&:hover': { + borderColor: theme.palette.primary.light, + }, + '&.selected': { + borderWidth: '3px', + borderColor: theme.palette.primary.light, + }, + }, +}); diff --git a/zubhub_frontend/zubhub/src/views/signup/steps/index.js b/zubhub_frontend/zubhub/src/views/signup/steps/index.js new file mode 100644 index 000000000..e970fbdef --- /dev/null +++ b/zubhub_frontend/zubhub/src/views/signup/steps/index.js @@ -0,0 +1,8 @@ +import Step1 from './step1/Step1'; +import Step2 from './step2/Step2'; +import Step3 from './step3/Step3'; +import Step4 from './step4/Step4'; +import Step5 from './step5/Step5'; +import Step6 from './step6/Step6'; + +export { Step1, Step2, Step3, Step4, Step5, Step6 }; diff --git a/zubhub_frontend/zubhub/src/views/signup/steps/step1/Step1.jsx b/zubhub_frontend/zubhub/src/views/signup/steps/step1/Step1.jsx new file mode 100644 index 000000000..071796c10 --- /dev/null +++ b/zubhub_frontend/zubhub/src/views/signup/steps/step1/Step1.jsx @@ -0,0 +1,83 @@ +import React from 'react'; +import clsx from 'clsx'; +import { Box, Grid, Paper, Typography } from '@mui/material'; +import { FaUserGraduate, FaChalkboardUser } from 'react-icons/fa6'; +import { makeStyles } from '@mui/styles'; +import { mainStyles, step1Styles } from '../../signupStyles'; +import { CustomButton } from '../../../../components'; + +const useMainStyles = makeStyles(mainStyles); +const useStepStyles = makeStyles(step1Styles); + +const Step1 = props => { + const mainClasses = useMainStyles(); + const classes = useStepStyles(); + const { + goAction, + values: { role }, + setFieldValue, + } = props; + + return ( + + + + + Welcome to ZubHub ! + + + Connect with students, educators, and the ZubHub community. To get started, choose an account type. + + + + + + setFieldValue('role', 'creator')} + variant="elevation" + elevation={2} + className={clsx([classes.paper, role === 'creator' && 'selected'])} + > + + Creator + I am a Creator, who wants to create + + + + setFieldValue('role', 'educator')} + className={clsx([classes.paper, role === 'educator' && 'selected'])} + variant="elevation" + elevetion={2} + > + + Educator + I am an Educator, looking to inspire + + + + + + goAction('next', false)} + > + {!role ? 'CHOOSE A ROLE' : role === 'creator' ? 'JOIN AS A CREATOR' : 'JOIN AS AN EDUCATOR'} + + + + + ); +}; + +export default Step1; diff --git a/zubhub_frontend/zubhub/src/views/signup/steps/step2/Step2.jsx b/zubhub_frontend/zubhub/src/views/signup/steps/step2/Step2.jsx new file mode 100644 index 000000000..0424c6f9e --- /dev/null +++ b/zubhub_frontend/zubhub/src/views/signup/steps/step2/Step2.jsx @@ -0,0 +1,166 @@ +import { + Box, + Grid, + FormControl, + FormLabel, + OutlinedInput, + InputAdornment, + Typography, + IconButton, +} from '@mui/material'; +import { CustomButton, CustomErrorMessage } from '../../../../components'; +import { TfiLock, TfiArrowLeft } from 'react-icons/tfi'; +import { SlEye, SlUser } from 'react-icons/sl'; +import { BsEyeSlash } from 'react-icons/bs'; +import { makeStyles } from '@mui/styles'; +import { useState } from 'react'; +import { mainStyles, step2Styles } from '../../signupStyles'; +import EducatorsForm from '../../EducatorsForm'; + +const useMainStyles = makeStyles(mainStyles); +const useStyles = makeStyles(step2Styles); +const Step2 = props => { + console.log(props, 'afafadh'); + const [{ password, confirmPassword }, setShowPassword] = useState({ password: false, confirmPassword: false }); + const classes = useStyles(); + const mainClasses = useMainStyles(); + const { + errors, + handleChange, + handleBlur, + goAction, + touched, + values: { role }, + } = props; + + const errorsAvailable = errors['username'] || errors['password'] || errors['confirmPassword']; + + if (role === 'educator') { + return ; + } + + return ( + + + + goAction('prev', false)} className={mainClasses.backContainer}> + + + + + + Welcome to ZubHub ! + + + Create projects, share ideas, make friends. It’s free{' '} + + + + + Username + + + + } + /> + + + + + + Password + + + + } + endAdornment={ + + setShowPassword({ password: !password })}> + {password ? ( + + ) : ( + + )} + + + } + /> + + + + + + Confirm Password + + + + } + endAdornment={ + + setShowPassword({ confirmPassword: !confirmPassword })}> + {confirmPassword ? ( + + ) : ( + + )} + + + } + /> + + + + + goAction('next', !!errorsAvailable)} + className={mainClasses.button} + primaryButtonStyle + fullWidth + > + NEXT + + + + + ); +}; + +export default Step2; diff --git a/zubhub_frontend/zubhub/src/views/signup/steps/step3/Step3.jsx b/zubhub_frontend/zubhub/src/views/signup/steps/step3/Step3.jsx new file mode 100644 index 000000000..6e1898da0 --- /dev/null +++ b/zubhub_frontend/zubhub/src/views/signup/steps/step3/Step3.jsx @@ -0,0 +1,114 @@ +// import { ArrowBackOutlined, LocationOnOutlined } from '@mui/icons-material'; +import { TfiLocationPin, TfiArrowLeft, TfiAngleDown, TfiAngleUp } from 'react-icons/tfi'; +import { + Box, + Button, + FormControl, + Grid, + IconButton, + InputAdornment, + InputLabel, + MenuItem, + OutlinedInput, + Select, + Typography, +} from '@mui/material'; +import { CustomButton, CustomErrorMessage } from '../../../../components'; +import SpaceBackground from '../../../../assets/images/space.png'; +import { makeStyles } from '@mui/styles'; +import { mainStyles, step3Styles } from '../../signupStyles'; + +const useMainStyles = makeStyles(mainStyles); +const useStyles = makeStyles(step3Styles); + +const Step3 = props => { + const mainClasses = useMainStyles(); + const { locations, errors, handleChange, handleBlur, goAction, touched } = props; + + return ( + + + + +
+ goAction('prev', false)} className={mainClasses.backContainer}> + + +
+
+ + + +
+ + + + What country do you live in? + + + + + + + + + + goAction('next', !!errors['location'])} + primaryButtonStyle + fullWidth + className={mainClasses.button} + > + Next + + + +
+
+ ); +}; + +export default Step3; diff --git a/zubhub_frontend/zubhub/src/views/signup/steps/step4/Step4.jsx b/zubhub_frontend/zubhub/src/views/signup/steps/step4/Step4.jsx new file mode 100644 index 000000000..713e1304a --- /dev/null +++ b/zubhub_frontend/zubhub/src/views/signup/steps/step4/Step4.jsx @@ -0,0 +1,107 @@ +import { Box, FormControl, Grid, Typography, IconButton, InputAdornment } from '@mui/material'; +import { CustomErrorMessage, CustomButton } from '../../../../components'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import { TfiArrowLeft, TfiAngleDown } from 'react-icons/tfi'; +import CreamBackground from '../../../../assets/images/cream.png'; +import { makeStyles } from '@mui/styles'; +import { mainStyles, step4Styles } from '../../signupStyles'; +import dayjs from 'dayjs'; +import 'dayjs/locale/de'; + +const useMainStyles = makeStyles(mainStyles); +const useStyles = makeStyles(step4Styles); + +const Step4 = props => { + const mainClasses = useMainStyles(); + const { + errors, + setFieldValue, + handleBlur, + goAction, + touched, + values: { dateOfBirth }, + } = props; + + const getMaxDate = () => { + const currentDate = new Date(); + const maxDate = new Date(); + maxDate.setFullYear(currentDate.getFullYear() - 12); + return dayjs(maxDate); + }; + + return ( + + + + +
+ goAction('prev', false)} className={mainClasses.backContainer}> + + +
+
+ + + +
+ + + + When were you born? + + + + + + setFieldValue('dateOfBirth', date)} + onBlur={handleBlur} + slotProps={{ + inputAdornment: { position: 'start' }, + textField: { + placeholder: 'dd/mm/yyyy', + }, + }} + /> + + + + + + goAction('next', !!errors['dateOfBirth'])} + > + Next + + + +
+
+ ); +}; + +export default Step4; diff --git a/zubhub_frontend/zubhub/src/views/signup/steps/step5/Step5.jsx b/zubhub_frontend/zubhub/src/views/signup/steps/step5/Step5.jsx new file mode 100644 index 000000000..e1f8dbf37 --- /dev/null +++ b/zubhub_frontend/zubhub/src/views/signup/steps/step5/Step5.jsx @@ -0,0 +1,83 @@ +import { Box, FormControl, FormControlLabel, Grid, Radio, Typography, IconButton, RadioGroup } from '@mui/material'; +import { CustomErrorMessage, CustomButton } from '../../../../components'; +import { TfiArrowLeft } from 'react-icons/tfi'; +import { makeStyles } from '@mui/styles'; +import { mainStyles, step5Styles } from '../../signupStyles'; + +const useMainStyles = makeStyles(mainStyles); +const useStyles = makeStyles(step5Styles); + +const Step5 = props => { + const mainClasses = useMainStyles(); + const classes = useStyles(); + const { errors, handleBlur, handleChange, goAction } = props; + return ( + + + + +
+ goAction('prev', false)} className={mainClasses.backContainer}> + + +
+
+ +
+ + + What’s your gender? + + + ZubHub welcomes people of all genders. + + + + + + + } label="Male" /> + + + } label="Female" /> + + + } label="Other" /> + + + } + label="Prefer not to say" + /> + + + + + + + goAction('next', !!errors['gender'])} + className={mainClasses.button} + primaryButtonStyle + fullWidth + > + NEXT + + +
+
+ ); +}; + +export default Step5; diff --git a/zubhub_frontend/zubhub/src/views/signup/steps/step6/Step6.jsx b/zubhub_frontend/zubhub/src/views/signup/steps/step6/Step6.jsx new file mode 100644 index 000000000..0879c159b --- /dev/null +++ b/zubhub_frontend/zubhub/src/views/signup/steps/step6/Step6.jsx @@ -0,0 +1,118 @@ +import { + Box, + Grid, + FormControl, + FormLabel, + OutlinedInput, + InputAdornment, + Typography, + FormControlLabel, + Checkbox, + IconButton, +} from '@mui/material'; +import { SlPhone, SlEnvolope } from 'react-icons/sl'; +import { TfiArrowLeft } from 'react-icons/tfi'; +import { CustomErrorMessage, CustomButton } from '../../../../components'; +import { makeStyles } from '@mui/styles'; +import { mainStyles, step4Styles } from '../../signupStyles'; + +const useMainStyles = makeStyles(mainStyles); +const useStyles = makeStyles(step4Styles); + +const Step6 = props => { + const mainClasses = useMainStyles(); + const { errors, handleChange, handleBlur, goAction, touched, handleSubmit } = props; + + return ( + + + + goAction('prev', false)} className={mainClasses.backContainer}> + + + + + + Parents or Guardian Details + + + + + Parent's Emaol + + + + } + /> + + + + + + Parent's Phone number + + + + } + /> + + + + + + } + /> + + + + + Create Your Account + + + + By signing up you agree to our Terms and Services and Privacy Policy + + + + ); +}; + +export default Step6;