UNPKG

react-garden

Version:

React + TypeScript + ThreeJS app using Material UI on NextJS, Apollo Client, GraphQL + WordPress REST APIs, for ThreeD web development.. a part of the threed.ai code family.

684 lines (652 loc) 26.5 kB
// ** React Imports import { Fragment, useState } from 'react' // ** MUI Imports import Box from '@mui/material/Box' import Card from '@mui/material/Card' import Step from '@mui/material/Step' import Grid from '@mui/material/Grid' import Button from '@mui/material/Button' import Select from '@mui/material/Select' import Divider from '@mui/material/Divider' import Stepper from '@mui/material/Stepper' import MenuItem from '@mui/material/MenuItem' import StepLabel from '@mui/material/StepLabel' import TextField from '@mui/material/TextField' import Typography from '@mui/material/Typography' import InputLabel from '@mui/material/InputLabel' import IconButton from '@mui/material/IconButton' import CardContent from '@mui/material/CardContent' import FormControl from '@mui/material/FormControl' import OutlinedInput from '@mui/material/OutlinedInput' import FormHelperText from '@mui/material/FormHelperText' import InputAdornment from '@mui/material/InputAdornment' // ** Third Party Imports import * as yup from 'yup' import toast from 'react-hot-toast' import { useForm, Controller } from 'react-hook-form' import { yupResolver } from '@hookform/resolvers/yup/dist/yup' // ** Icons Imports import EyeOutline from 'mdi-material-ui/EyeOutline' import EyeOffOutline from 'mdi-material-ui/EyeOffOutline' // ** Styled Components import StepperWrapper from '~/@core/styles/mui/stepper' // ** Custom Components Imports import StepperCustomDot from './StepperCustomDot' const steps = [ { title: 'Account Details', subtitle: 'Enter your Account Details' }, { title: 'Personal Info', subtitle: 'Setup Information' }, { title: 'Social Links', subtitle: 'Add Social Links' } ] const defaultAccountValues = { email: '', username: '', password: '', 'confirm-password': '' } const defaultPersonalValues = { country: '', language: [], 'last-name': '', 'first-name': '' } const defaultSocialValues = { google: '', twitter: '', facebook: '', linkedIn: '' } const accountSchema = yup.object().shape({ username: yup.string().required(), email: yup.string().email().required(), password: yup.string().min(6).required(), 'confirm-password': yup .string() .required() .oneOf([yup.ref('password'), null], 'Passwords must match') }) const personalSchema = yup.object().shape({ country: yup.string().required(), 'last-name': yup.string().required(), 'first-name': yup.string().required(), language: yup.array().min(1).required() }) const socialSchema = yup.object().shape({ google: yup.string().required(), twitter: yup.string().required(), facebook: yup.string().required(), linkedIn: yup.string().required() }) const StepperLinearWithValidation = () => { // ** States const [activeStep, setActiveStep] = useState(0) const [state, setState] = useState({ password: '', password2: '', showPassword: false, showPassword2: false }) // ** Hooks const { reset: accountReset, control: accountControl, handleSubmit: handleAccountSubmit, formState: { errors: accountErrors } } = useForm({ defaultValues: defaultAccountValues, resolver: yupResolver(accountSchema) }) const { reset: personalReset, control: personalControl, handleSubmit: handlePersonalSubmit, formState: { errors: personalErrors } } = useForm({ defaultValues: defaultPersonalValues, resolver: yupResolver(personalSchema) }) const { reset: socialReset, control: socialControl, handleSubmit: handleSocialSubmit, formState: { errors: socialErrors } } = useForm({ defaultValues: defaultSocialValues, resolver: yupResolver(socialSchema) }) // Handle Stepper const handleBack = () => { setActiveStep(prevActiveStep => prevActiveStep - 1) } const handleReset = () => { setActiveStep(0) socialReset({ google: '', twitter: '', facebook: '', linkedIn: '' }) accountReset({ email: '', username: '', password: '', 'confirm-password': '' }) personalReset({ country: '', language: [], 'last-name': '', 'first-name': '' }) } const onSubmit = () => { setActiveStep(activeStep + 1) if (activeStep === steps.length - 1) { toast.success('Form Submitted') } } // Handle Password const handleClickShowPassword = () => { setState({ ...state, showPassword: !state.showPassword }) } const handleMouseDownPassword = event => { event.preventDefault() } // Handle Confirm Password const handleClickShowConfirmPassword = () => { setState({ ...state, showPassword2: !state.showPassword2 }) } const handleMouseDownConfirmPassword = event => { event.preventDefault() } const getStepContent = step => { switch (step) { case 0: return ( <form key={0} onSubmit={handleAccountSubmit(onSubmit)}> <Grid container spacing={5}> <Grid item xs={12}> <Typography variant='body2' sx={{ fontWeight: 600, color: 'text.primary' }}> {steps[0].title} </Typography> <Typography variant='caption' component='p'> {steps[0].subtitle} </Typography> </Grid> <Grid item xs={12} sm={6}> <FormControl fullWidth> <Controller name='username' control={accountControl} rules={{ required: true }} render={({ field: { value, onChange } }) => ( <TextField value={value} label='Username' onChange={onChange} placeholder='carterLeonard' error={Boolean(accountErrors.username)} aria-describedby='stepper-linear-account-username' /> )} /> {accountErrors.username && ( <FormHelperText sx={{ color: 'error.main' }} id='stepper-linear-account-username'> This field is required </FormHelperText> )} </FormControl> </Grid> <Grid item xs={12} sm={6}> <FormControl fullWidth> <Controller name='email' control={accountControl} rules={{ required: true }} render={({ field: { value, onChange } }) => ( <TextField type='email' value={value} label='Email' onChange={onChange} error={Boolean(accountErrors.email)} placeholder='carterleonard@gmail.com' aria-describedby='stepper-linear-account-email' /> )} /> {accountErrors.email && ( <FormHelperText sx={{ color: 'error.main' }} id='stepper-linear-account-email'> {accountErrors.email.message} </FormHelperText> )} </FormControl> </Grid> <Grid item xs={12} sm={6}> <FormControl fullWidth> <InputLabel htmlFor='stepper-linear-account-password' error={Boolean(accountErrors.password)}> Password </InputLabel> <Controller name='password' control={accountControl} rules={{ required: true }} render={({ field: { value, onChange } }) => ( <OutlinedInput value={value} label='Password' onChange={onChange} id='stepper-linear-account-password' error={Boolean(accountErrors.password)} type={state.showPassword ? 'text' : 'password'} endAdornment={ <InputAdornment position='end'> <IconButton edge='end' onClick={handleClickShowPassword} onMouseDown={handleMouseDownPassword} aria-label='toggle password visibility' > {state.showPassword ? <EyeOutline /> : <EyeOffOutline />} </IconButton> </InputAdornment> } /> )} /> {accountErrors.password && ( <FormHelperText sx={{ color: 'error.main' }} id='stepper-linear-account-password-helper'> {accountErrors.password.message} </FormHelperText> )} </FormControl> </Grid> <Grid item xs={12} sm={6}> <FormControl fullWidth> <InputLabel htmlFor='stepper-linear-account-confirm-password' error={Boolean(accountErrors['confirm-password'])} > Confirm Password </InputLabel> <Controller name='confirm-password' control={accountControl} rules={{ required: true }} render={({ field: { value, onChange } }) => ( <OutlinedInput value={value} onChange={onChange} label='Confirm Password' id='stepper-linear-account-confirm-password' type={state.showPassword2 ? 'text' : 'password'} error={Boolean(accountErrors['confirm-password'])} endAdornment={ <InputAdornment position='end'> <IconButton edge='end' aria-label='toggle password visibility' onClick={handleClickShowConfirmPassword} onMouseDown={handleMouseDownConfirmPassword} > {state.showPassword2 ? <EyeOutline /> : <EyeOffOutline />} </IconButton> </InputAdornment> } /> )} /> {accountErrors['confirm-password'] && ( <FormHelperText sx={{ color: 'error.main' }} id='stepper-linear-account-confirm-password-helper'> {accountErrors['confirm-password'].message} </FormHelperText> )} </FormControl> </Grid> <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}> <Button size='large' variant='outlined' color='secondary' disabled> Back </Button> <Button size='large' type='submit' variant='contained'> Next </Button> </Grid> </Grid> </form> ) case 1: return ( <form key={1} onSubmit={handlePersonalSubmit(onSubmit)}> <Grid container spacing={5}> <Grid item xs={12}> <Typography variant='body2' sx={{ fontWeight: 600, color: 'text.primary' }}> {steps[1].title} </Typography> <Typography variant='caption' component='p'> {steps[1].subtitle} </Typography> </Grid> <Grid item xs={12} sm={6}> <FormControl fullWidth> <Controller name='first-name' control={personalControl} rules={{ required: true }} render={({ field: { value, onChange } }) => ( <TextField value={value} label='First Name' onChange={onChange} placeholder='Leonard' error={Boolean(personalErrors['first-name'])} aria-describedby='stepper-linear-personal-first-name' /> )} /> {personalErrors['first-name'] && ( <FormHelperText sx={{ color: 'error.main' }} id='stepper-linear-personal-first-name'> This field is required </FormHelperText> )} </FormControl> </Grid> <Grid item xs={12} sm={6}> <FormControl fullWidth> <Controller name='last-name' control={personalControl} rules={{ required: true }} render={({ field: { value, onChange } }) => ( <TextField value={value} label='Last Name' onChange={onChange} placeholder='Carter' error={Boolean(personalErrors['last-name'])} aria-describedby='stepper-linear-personal-last-name' /> )} /> {personalErrors['last-name'] && ( <FormHelperText sx={{ color: 'error.main' }} id='stepper-linear-personal-last-name'> This field is required </FormHelperText> )} </FormControl> </Grid> <Grid item xs={12} sm={6}> <FormControl fullWidth> <InputLabel id='stepper-linear-personal-country' error={Boolean(personalErrors.country)} htmlFor='stepper-linear-personal-country' > Country </InputLabel> <Controller name='country' control={personalControl} rules={{ required: true }} render={({ field: { value, onChange } }) => ( <Select value={value} label='Country' onChange={onChange} error={Boolean(personalErrors.country)} labelId='stepper-linear-personal-country' aria-describedby='stepper-linear-personal-country-helper' > <MenuItem value='UK'>UK</MenuItem> <MenuItem value='USA'>USA</MenuItem> <MenuItem value='Australia'>Australia</MenuItem> <MenuItem value='Germany'>Germany</MenuItem> </Select> )} /> {personalErrors.country && ( <FormHelperText sx={{ color: 'error.main' }} id='stepper-linear-personal-country-helper'> This field is required </FormHelperText> )} </FormControl> </Grid> <Grid item xs={12} sm={6}> <FormControl fullWidth> <InputLabel error={Boolean(personalErrors.language)} htmlFor='stepper-linear-personal-language' id='stepper-linear-personal-language-label' > Language </InputLabel> <Controller name='language' control={personalControl} rules={{ required: true }} render={({ field: { value, onChange } }) => ( <Select multiple onChange={onChange} id='stepper-linear-personal-language' value={Array.isArray(value) ? value : []} error={Boolean(personalErrors.language)} labelId='stepper-linear-personal-language-label' input={<OutlinedInput label='Language' id='stepper-linear-select-multiple-language' />} > <MenuItem value='English'>English</MenuItem> <MenuItem value='French'>French</MenuItem> <MenuItem value='Spanish'>Spanish</MenuItem> <MenuItem value='Portuguese'>Portuguese</MenuItem> <MenuItem value='Italian'>Italian</MenuItem> <MenuItem value='German'>German</MenuItem> <MenuItem value='Arabic'>Arabic</MenuItem> </Select> )} /> {personalErrors.language && ( <FormHelperText sx={{ color: 'error.main' }} id='stepper-linear-personal-language-helper'> This field is required </FormHelperText> )} </FormControl> </Grid> <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}> <Button size='large' variant='outlined' color='secondary' onClick={handleBack}> Back </Button> <Button size='large' type='submit' variant='contained'> Next </Button> </Grid> </Grid> </form> ) case 2: return ( <form key={2} onSubmit={handleSocialSubmit(onSubmit)}> <Grid container spacing={5}> <Grid item xs={12}> <Typography variant='body2' sx={{ fontWeight: 600, color: 'text.primary' }}> {steps[2].title} </Typography> <Typography variant='caption' component='p'> {steps[2].subtitle} </Typography> </Grid> <Grid item xs={12} sm={6}> <FormControl fullWidth> <Controller name='twitter' control={socialControl} rules={{ required: true }} render={({ field: { value, onChange } }) => ( <TextField value={value} label='Twitter' onChange={onChange} error={Boolean(socialErrors.twitter)} placeholder='https://twitter.com/carterLeonard' aria-describedby='stepper-linear-social-twitter' /> )} /> {socialErrors.twitter && ( <FormHelperText sx={{ color: 'error.main' }} id='stepper-linear-social-twitter'> This field is required </FormHelperText> )} </FormControl> </Grid> <Grid item xs={12} sm={6}> <FormControl fullWidth> <Controller name='facebook' control={socialControl} rules={{ required: true }} render={({ field: { value, onChange } }) => ( <TextField value={value} label='Facebook' onChange={onChange} error={Boolean(socialErrors.facebook)} placeholder='https://facebook.com/carterLeonard' aria-describedby='stepper-linear-social-facebook' /> )} /> {socialErrors.facebook && ( <FormHelperText sx={{ color: 'error.main' }} id='stepper-linear-social-facebook'> This field is required </FormHelperText> )} </FormControl> </Grid> <Grid item xs={12} sm={6}> <FormControl fullWidth> <Controller name='google' control={socialControl} rules={{ required: true }} render={({ field: { value, onChange } }) => ( <TextField value={value} label='Google+' onChange={onChange} error={Boolean(socialErrors.google)} aria-describedby='stepper-linear-social-google' placeholder='https://plus.google.com/carterLeonard' /> )} /> {socialErrors.google && ( <FormHelperText sx={{ color: 'error.main' }} id='stepper-linear-social-google'> This field is required </FormHelperText> )} </FormControl> </Grid> <Grid item xs={12} sm={6}> <FormControl fullWidth> <Controller name='linkedIn' control={socialControl} rules={{ required: true }} render={({ field: { value, onChange } }) => ( <TextField value={value} label='LinkedIn' onChange={onChange} error={Boolean(socialErrors.linkedIn)} placeholder='https://linkedin.com/carterLeonard' aria-describedby='stepper-linear-social-linkedIn' /> )} /> {socialErrors.linkedIn && ( <FormHelperText sx={{ color: 'error.main' }} id='stepper-linear-social-linkedIn'> This field is required </FormHelperText> )} </FormControl> </Grid> <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}> <Button size='large' variant='outlined' color='secondary' onClick={handleBack}> Back </Button> <Button size='large' type='submit' variant='contained'> Submit </Button> </Grid> </Grid> </form> ) default: return null } } const renderContent = () => { if (activeStep === steps.length) { return ( <> <Typography>All steps are completed!</Typography> <Box sx={{ mt: 4, display: 'flex', justifyContent: 'flex-end' }}> <Button size='large' variant='contained' onClick={handleReset}> Reset </Button> </Box> </> ) } else { return getStepContent(activeStep) } } return ( <Card> <CardContent> <StepperWrapper> <Stepper activeStep={activeStep}> {steps.map((step, index) => { const labelProps = {} if (index === activeStep) { labelProps.error = false if ( (accountErrors.email || accountErrors.username || accountErrors.password || accountErrors['confirm-password']) && activeStep === 0 ) { labelProps.error = true } else if ( (personalErrors.country || personalErrors.language || personalErrors['last-name'] || personalErrors['first-name']) && activeStep === 1 ) { labelProps.error = true } else if ( (socialErrors.google || socialErrors.twitter || socialErrors.facebook || socialErrors.linkedIn) && activeStep === 2 ) { labelProps.error = true } else { labelProps.error = false } } return ( <Step key={index}> <StepLabel {...labelProps} StepIconComponent={StepperCustomDot}> <div className='step-label'> <Typography className='step-number'>0{index + 1}</Typography> <div> <Typography className='step-title'>{step.title}</Typography> <Typography className='step-subtitle'>{step.subtitle}</Typography> </div> </div> </StepLabel> </Step> ) })} </Stepper> </StepperWrapper> </CardContent> <Divider sx={{ m: 0 }} /> <CardContent>{renderContent()}</CardContent> </Card> ) } export default StepperLinearWithValidation