initial commit
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Button from '@mui/material/Button'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Checkbox from '@mui/material/Checkbox'
|
||||
import CardHeader from '@mui/material/CardHeader'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import FormControlLabel from '@mui/material/FormControlLabel'
|
||||
import InputAdornment from '@mui/material/InputAdornment'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
|
||||
// Components Imports
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
const FormLayoutsAlignment = () => {
|
||||
// States
|
||||
const [isPasswordShown, setIsPasswordShown] = useState(false)
|
||||
|
||||
const handleClickShowPassword = () => setIsPasswordShown(show => !show)
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Form Alignment' />
|
||||
<CardContent className='flex flex-col items-center justify-center bs-[500px]'>
|
||||
<form onSubmit={e => e.preventDefault()} className='p-12 max-is-[400px] border rounded'>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Typography variant='h5'>Sign In</Typography>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField fullWidth label='Username' placeholder='johnDoe ' />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Password'
|
||||
placeholder='············'
|
||||
id='form-layout-alignment-password'
|
||||
type={isPasswordShown ? 'text' : 'password'}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle password visibility'
|
||||
>
|
||||
<i className={isPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }} className='pbs-2'>
|
||||
<FormControlLabel control={<Checkbox />} label='Remember me' />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }} className='pbs-2'>
|
||||
<Button variant='contained' type='submit' fullWidth>
|
||||
Log In
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormLayoutsAlignment
|
||||
@@ -0,0 +1,121 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// Next Imports
|
||||
import Link from 'next/link'
|
||||
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Button from '@mui/material/Button'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import CardHeader from '@mui/material/CardHeader'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import InputAdornment from '@mui/material/InputAdornment'
|
||||
|
||||
// Components Imports
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
const FormLayoutsBasic = () => {
|
||||
// States
|
||||
const [isPasswordShown, setIsPasswordShown] = useState(false)
|
||||
const [isConfirmPasswordShown, setIsConfirmPasswordShown] = useState(false)
|
||||
|
||||
const handleClickShowPassword = () => setIsPasswordShown(show => !show)
|
||||
|
||||
const handleClickShowConfirmPassword = () => setIsConfirmPasswordShown(show => !show)
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Basic' />
|
||||
<CardContent>
|
||||
<form onSubmit={e => e.preventDefault()}>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField fullWidth label='Name' placeholder='John Doe' />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
type='email'
|
||||
label='Email'
|
||||
placeholder='johndoe@gmail.com'
|
||||
helperText='You can use letters, numbers & periods'
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Password'
|
||||
placeholder='············'
|
||||
id='form-layout-basic-password'
|
||||
type={isPasswordShown ? 'text' : 'password'}
|
||||
helperText='Use 8 or more characters with a mix of letters, numbers & symbols'
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle password visibility'
|
||||
>
|
||||
<i className={isPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Confirm Password'
|
||||
placeholder='············'
|
||||
id='form-layout-basic-confirm-password'
|
||||
type={isConfirmPasswordShown ? 'text' : 'password'}
|
||||
helperText='Make sure to type the same password as above'
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowConfirmPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle confirm password visibility'
|
||||
>
|
||||
<i className={isConfirmPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<div className='flex items-center justify-between flex-wrap gap-5'>
|
||||
<Button variant='contained' type='submit'>
|
||||
Get Started!
|
||||
</Button>
|
||||
<div className='flex items-center justify-center gap-2'>
|
||||
<Typography>Already have an account?</Typography>
|
||||
<Link href='/' onClick={e => e.preventDefault()} className='text-primary'>
|
||||
Log In
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormLayoutsBasic
|
||||
@@ -0,0 +1,332 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
import type { ChangeEvent, SyntheticEvent } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Button from '@mui/material/Button'
|
||||
import Accordion from '@mui/material/Accordion'
|
||||
import Radio from '@mui/material/Radio'
|
||||
import MenuItem from '@mui/material/MenuItem'
|
||||
import Divider from '@mui/material/Divider'
|
||||
import FormLabel from '@mui/material/FormLabel'
|
||||
import FormControlLabel from '@mui/material/FormControlLabel'
|
||||
import RadioGroup from '@mui/material/RadioGroup'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import AccordionSummary from '@mui/material/AccordionSummary'
|
||||
import AccordionDetails from '@mui/material/AccordionDetails'
|
||||
|
||||
// Type Imports
|
||||
import type { CustomInputHorizontalData } from '@core/components/custom-inputs/types'
|
||||
|
||||
// Component Imports
|
||||
import CustomInputHorizontal from '@core/components/custom-inputs/Horizontal'
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
type FormData = {
|
||||
fullName: string
|
||||
phone: string
|
||||
address: string
|
||||
zipCode: string
|
||||
landmark: string
|
||||
city: string
|
||||
country: string
|
||||
addressType: string
|
||||
number: string
|
||||
name: string
|
||||
expiry: string
|
||||
cvv: string
|
||||
}
|
||||
|
||||
// Vars
|
||||
const data: CustomInputHorizontalData[] = [
|
||||
{
|
||||
title: 'Standard 3-5 Days',
|
||||
meta: 'Free',
|
||||
content: 'Friday, 15 Nov - Monday, 18 Nov',
|
||||
isSelected: true,
|
||||
value: 'standard'
|
||||
},
|
||||
{
|
||||
title: 'Express',
|
||||
meta: '$5.00',
|
||||
content: 'Friday, 15 Nov - Sunday, 17 Nov',
|
||||
value: 'express'
|
||||
},
|
||||
{
|
||||
title: 'Overnight',
|
||||
meta: '$10.00',
|
||||
content: 'Friday, 15 Nov - Saturday, 16 Nov',
|
||||
value: 'overnight'
|
||||
}
|
||||
]
|
||||
|
||||
const FormLayoutsCollapsible = () => {
|
||||
// Vars
|
||||
const initialSelectedOption: string = data.filter(item => item.isSelected)[
|
||||
data.filter(item => item.isSelected).length - 1
|
||||
].value
|
||||
|
||||
// States
|
||||
const [expanded, setExpanded] = useState<string | false>('panel1')
|
||||
const [paymentMethod, setPaymentMethod] = useState('credit')
|
||||
const [selectedOption, setSelectedOption] = useState<string>(initialSelectedOption)
|
||||
|
||||
const [cardData, setCardData] = useState<FormData>({
|
||||
fullName: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
zipCode: '',
|
||||
landmark: '',
|
||||
city: '',
|
||||
country: '',
|
||||
addressType: 'home',
|
||||
number: '',
|
||||
name: '',
|
||||
expiry: '',
|
||||
cvv: ''
|
||||
})
|
||||
|
||||
const handleExpandChange = (panel: string) => (event: SyntheticEvent, isExpanded: boolean) => {
|
||||
setExpanded(isExpanded ? panel : false)
|
||||
}
|
||||
|
||||
const handleOptionChange = (prop: string | ChangeEvent<HTMLInputElement>) => {
|
||||
if (typeof prop === 'string') {
|
||||
setSelectedOption(prop)
|
||||
} else {
|
||||
setSelectedOption((prop.target as HTMLInputElement).value)
|
||||
}
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
setCardData({
|
||||
fullName: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
zipCode: '',
|
||||
landmark: '',
|
||||
city: '',
|
||||
country: '',
|
||||
addressType: '',
|
||||
number: '',
|
||||
name: '',
|
||||
expiry: '',
|
||||
cvv: ''
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={e => e.preventDefault()}>
|
||||
<Accordion expanded={expanded === 'panel1'} onChange={handleExpandChange('panel1')}>
|
||||
<AccordionSummary expandIcon={<i className='tabler-chevron-right' />}>
|
||||
<Typography>Delivery Address</Typography>
|
||||
</AccordionSummary>
|
||||
<Divider />
|
||||
<AccordionDetails className='!pbs-6'>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Full Name'
|
||||
placeholder='John Doe'
|
||||
value={cardData.fullName}
|
||||
onChange={e => setCardData({ ...cardData, fullName: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Phone No.'
|
||||
placeholder='123-456-7890'
|
||||
value={cardData.phone}
|
||||
onChange={e => setCardData({ ...cardData, phone: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
rows={4}
|
||||
multiline
|
||||
label='Address'
|
||||
placeholder='1456, Liberty Street'
|
||||
value={cardData.address}
|
||||
onChange={e => setCardData({ ...cardData, address: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
type='number'
|
||||
label='ZIP Code'
|
||||
placeholder='10005'
|
||||
value={cardData.zipCode}
|
||||
onChange={e => setCardData({ ...cardData, zipCode: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Landmark'
|
||||
placeholder='Nr Wall Street'
|
||||
value={cardData.landmark}
|
||||
onChange={e => setCardData({ ...cardData, landmark: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='City'
|
||||
placeholder='New York'
|
||||
value={cardData.city}
|
||||
onChange={e => setCardData({ ...cardData, city: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='Country'
|
||||
value={cardData.country}
|
||||
onChange={e => setCardData({ ...cardData, country: e.target.value })}
|
||||
>
|
||||
<MenuItem value=''>Select Country</MenuItem>
|
||||
<MenuItem value='UK'>UK</MenuItem>
|
||||
<MenuItem value='USA'>USA</MenuItem>
|
||||
<MenuItem value='Australia'>Australia</MenuItem>
|
||||
<MenuItem value='Germany'>Germany</MenuItem>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<FormLabel>Address Type</FormLabel>
|
||||
<RadioGroup
|
||||
row
|
||||
name='radio-buttons-group'
|
||||
value={cardData.addressType}
|
||||
onChange={e => setCardData({ ...cardData, addressType: e.target.value })}
|
||||
>
|
||||
<FormControlLabel value='home' control={<Radio />} label='Home (All day delivery)' />
|
||||
<FormControlLabel value='office' control={<Radio />} label='Office (Delivery between 10 AM - 5 PM)' />
|
||||
</RadioGroup>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
||||
<Accordion expanded={expanded === 'panel2'} onChange={handleExpandChange('panel2')}>
|
||||
<AccordionSummary expandIcon={<i className='tabler-chevron-right' />}>
|
||||
<Typography>Delivery Options</Typography>
|
||||
</AccordionSummary>
|
||||
<Divider />
|
||||
<AccordionDetails className='!pbs-6'>
|
||||
<Grid container>
|
||||
{data.map((item, index) => (
|
||||
<CustomInputHorizontal
|
||||
type='radio'
|
||||
key={index}
|
||||
data={item}
|
||||
gridProps={{
|
||||
size: { xs: 12 },
|
||||
className:
|
||||
'[&:first-of-type>*]:rounded-be-none [&:last-of-type>*]:rounded-bs-none [&:nth-of-type(2)>*]:rounded-none'
|
||||
}}
|
||||
selected={selectedOption}
|
||||
name='custom-radios-basic'
|
||||
handleChange={handleOptionChange}
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
||||
<Accordion expanded={expanded === 'panel3'} onChange={handleExpandChange('panel3')}>
|
||||
<AccordionSummary expandIcon={<i className='tabler-chevron-right' />}>
|
||||
<Typography>Payment Method</Typography>
|
||||
</AccordionSummary>
|
||||
<Divider />
|
||||
<AccordionDetails className='!pbs-6'>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<RadioGroup
|
||||
row
|
||||
name='payment-method-radio'
|
||||
value={paymentMethod}
|
||||
onChange={e => setPaymentMethod(e.target.value)}
|
||||
>
|
||||
<FormControlLabel value='credit' control={<Radio />} label='Credit/Debit/ATM Card' />
|
||||
<FormControlLabel value='cash' control={<Radio />} label='Cash on Delivery' />
|
||||
</RadioGroup>
|
||||
</Grid>
|
||||
{paymentMethod === 'credit' ? (
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
name='number'
|
||||
autoComplete='off'
|
||||
label='Card Number'
|
||||
placeholder='0000 0000 0000 0000'
|
||||
value={cardData.number}
|
||||
onChange={e => setCardData({ ...cardData, number: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
name='name'
|
||||
label='Name'
|
||||
autoComplete='off'
|
||||
placeholder='John Doe'
|
||||
value={cardData.name}
|
||||
onChange={e => setCardData({ ...cardData, name: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
name='expiry'
|
||||
autoComplete='off'
|
||||
label='Expiry Date'
|
||||
placeholder='MM/YY'
|
||||
value={cardData.expiry}
|
||||
onChange={e => setCardData({ ...cardData, expiry: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
name='cvv'
|
||||
label='CVV Code'
|
||||
autoComplete='off'
|
||||
placeholder='123'
|
||||
value={cardData.cvv}
|
||||
onChange={e => setCardData({ ...cardData, cvv: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
) : null}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</AccordionDetails>
|
||||
<Divider />
|
||||
<AccordionDetails className='flex gap-4 pbs-6'>
|
||||
<Button type='submit' variant='contained'>
|
||||
Place Order
|
||||
</Button>
|
||||
<Button type='reset' variant='tonal' color='secondary' onClick={() => handleReset()}>
|
||||
Reset
|
||||
</Button>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormLayoutsCollapsible
|
||||
@@ -0,0 +1,100 @@
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Button from '@mui/material/Button'
|
||||
import CardHeader from '@mui/material/CardHeader'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import InputAdornment from '@mui/material/InputAdornment'
|
||||
|
||||
// Components Imports
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
import Form from '@components/Form'
|
||||
|
||||
const FormLayoutsWithIcon = () => {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Basic with Icons' />
|
||||
<CardContent>
|
||||
<Form>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Name'
|
||||
placeholder='John Doe'
|
||||
slotProps={{
|
||||
input: {
|
||||
startAdornment: (
|
||||
<InputAdornment position='start'>
|
||||
<i className='tabler-user' />
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
type='email'
|
||||
label='Email'
|
||||
placeholder='johndoe@gmail.com'
|
||||
slotProps={{
|
||||
input: {
|
||||
startAdornment: (
|
||||
<InputAdornment position='start'>
|
||||
<i className='tabler-mail' />
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Phone No.'
|
||||
placeholder='123-456-7890'
|
||||
slotProps={{
|
||||
input: {
|
||||
startAdornment: (
|
||||
<InputAdornment position='start'>
|
||||
<i className='tabler-phone' />
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
rows={4}
|
||||
multiline
|
||||
label='Message'
|
||||
placeholder='Bio...'
|
||||
sx={{ '& .MuiInputBase-root.MuiFilledInput-root': { alignItems: 'baseline' } }}
|
||||
slotProps={{
|
||||
input: {
|
||||
startAdornment: (
|
||||
<InputAdornment position='start'>
|
||||
<i className='tabler-message' />
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Button variant='contained' type='submit'>
|
||||
Submit
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormLayoutsWithIcon
|
||||
@@ -0,0 +1,270 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Button from '@mui/material/Button'
|
||||
import Divider from '@mui/material/Divider'
|
||||
import MenuItem from '@mui/material/MenuItem'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import CardHeader from '@mui/material/CardHeader'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import CardActions from '@mui/material/CardActions'
|
||||
import InputAdornment from '@mui/material/InputAdornment'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
|
||||
// Components Imports
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
// Styled Component Imports
|
||||
import AppReactDatepicker from '@/libs/styles/AppReactDatepicker'
|
||||
|
||||
type FormDataType = {
|
||||
username: string
|
||||
email: string
|
||||
password: string
|
||||
isPasswordShown: boolean
|
||||
confirmPassword: string
|
||||
isConfirmPasswordShown: boolean
|
||||
firstName: string
|
||||
lastName: string
|
||||
country: string
|
||||
language: string[]
|
||||
date: Date | null
|
||||
phoneNumber: string
|
||||
}
|
||||
|
||||
const FormLayoutsSeparator = () => {
|
||||
// States
|
||||
const [formData, setFormData] = useState<FormDataType>({
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
isPasswordShown: false,
|
||||
confirmPassword: '',
|
||||
isConfirmPasswordShown: false,
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
country: '',
|
||||
language: [],
|
||||
date: null,
|
||||
phoneNumber: ''
|
||||
})
|
||||
|
||||
const handleClickShowPassword = () => setFormData(show => ({ ...show, isPasswordShown: !show.isPasswordShown }))
|
||||
|
||||
const handleClickShowConfirmPassword = () =>
|
||||
setFormData(show => ({ ...show, isConfirmPasswordShown: !show.isConfirmPasswordShown }))
|
||||
|
||||
const handleReset = () => {
|
||||
setFormData({
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
isPasswordShown: false,
|
||||
confirmPassword: '',
|
||||
isConfirmPasswordShown: false,
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
country: '',
|
||||
language: [],
|
||||
date: null,
|
||||
phoneNumber: ''
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Multi Column with Form Separator' />
|
||||
<Divider />
|
||||
<form onSubmit={e => e.preventDefault()}>
|
||||
<CardContent>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Typography variant='body2' className='font-medium'>
|
||||
1. Account Details
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='UserName'
|
||||
placeholder='johnDoe '
|
||||
value={formData.username}
|
||||
onChange={e => setFormData({ ...formData, username: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
type='email'
|
||||
label='Email'
|
||||
value={formData.email}
|
||||
placeholder='johndoe@gmail.com'
|
||||
onChange={e => setFormData({ ...formData, email: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Password'
|
||||
placeholder='············'
|
||||
id='form-layout-separator-password'
|
||||
type={formData.isPasswordShown ? 'text' : 'password'}
|
||||
value={formData.password}
|
||||
onChange={e => setFormData({ ...formData, password: e.target.value })}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle password visibility'
|
||||
>
|
||||
<i className={formData.isPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Confirm Password'
|
||||
placeholder='············'
|
||||
id='form-layout-separator-confirm-password'
|
||||
type={formData.isConfirmPasswordShown ? 'text' : 'password'}
|
||||
value={formData.confirmPassword}
|
||||
onChange={e => setFormData({ ...formData, confirmPassword: e.target.value })}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowConfirmPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle confirm password visibility'
|
||||
>
|
||||
<i className={formData.isConfirmPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Typography variant='body2' className='font-medium'>
|
||||
2. Personal Info
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='First Name'
|
||||
placeholder='John'
|
||||
value={formData.firstName}
|
||||
onChange={e => setFormData({ ...formData, firstName: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Last Name'
|
||||
placeholder='Doe'
|
||||
value={formData.lastName}
|
||||
onChange={e => setFormData({ ...formData, lastName: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='Country'
|
||||
value={formData.country}
|
||||
onChange={e => setFormData({ ...formData, country: e.target.value })}
|
||||
>
|
||||
<MenuItem value=''>Select Country</MenuItem>
|
||||
<MenuItem value='UK'>UK</MenuItem>
|
||||
<MenuItem value='USA'>USA</MenuItem>
|
||||
<MenuItem value='Australia'>Australia</MenuItem>
|
||||
<MenuItem value='Germany'>Germany</MenuItem>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='Language'
|
||||
value={formData.language}
|
||||
slotProps={{
|
||||
select: {
|
||||
multiple: true,
|
||||
onChange: e => setFormData({ ...formData, language: e.target.value as string[] })
|
||||
}
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<AppReactDatepicker
|
||||
selected={formData.date}
|
||||
showYearDropdown
|
||||
showMonthDropdown
|
||||
onChange={(date: Date | null) => setFormData({ ...formData, date })}
|
||||
placeholderText='MM/DD/YYYY'
|
||||
customInput={<CustomTextField fullWidth label='Birth Date' placeholder='MM-DD-YYYY' />}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Phone Number'
|
||||
type='number'
|
||||
placeholder='123-456-7890'
|
||||
value={formData.phoneNumber}
|
||||
onChange={e => setFormData({ ...formData, phoneNumber: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
<Divider />
|
||||
<CardActions>
|
||||
<Button type='submit' variant='contained' className='mie-2'>
|
||||
Submit
|
||||
</Button>
|
||||
<Button
|
||||
type='reset'
|
||||
variant='tonal'
|
||||
color='secondary'
|
||||
onClick={() => {
|
||||
handleReset()
|
||||
}}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
</CardActions>
|
||||
</form>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormLayoutsSeparator
|
||||
@@ -0,0 +1,346 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
import type { SyntheticEvent } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Button from '@mui/material/Button'
|
||||
import Tab from '@mui/material/Tab'
|
||||
import MenuItem from '@mui/material/MenuItem'
|
||||
import TabContext from '@mui/lab/TabContext'
|
||||
import TabList from '@mui/lab/TabList'
|
||||
import TabPanel from '@mui/lab/TabPanel'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import CardActions from '@mui/material/CardActions'
|
||||
import Divider from '@mui/material/Divider'
|
||||
import InputAdornment from '@mui/material/InputAdornment'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
|
||||
// Components Imports
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
// Styled Component Imports
|
||||
import AppReactDatepicker from '@/libs/styles/AppReactDatepicker'
|
||||
|
||||
type FormDataType = {
|
||||
firstName: string
|
||||
lastName: string
|
||||
country: string
|
||||
language: string[]
|
||||
date: Date | null
|
||||
phoneNumber: string
|
||||
username: string
|
||||
email: string
|
||||
password: string
|
||||
isPasswordShown: boolean
|
||||
confirmPassword: string
|
||||
setIsConfirmPasswordShown: boolean
|
||||
twitter: string
|
||||
facebook: string
|
||||
google: string
|
||||
linkedin: string
|
||||
instagram: string
|
||||
quora: string
|
||||
}
|
||||
|
||||
const FormLayoutsWithTabs = () => {
|
||||
// States
|
||||
const [value, setValue] = useState('personal_info')
|
||||
|
||||
const [formData, setFormData] = useState<FormDataType>({
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
country: '',
|
||||
language: [],
|
||||
date: null,
|
||||
phoneNumber: '',
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
isPasswordShown: false,
|
||||
confirmPassword: '',
|
||||
setIsConfirmPasswordShown: false,
|
||||
twitter: '',
|
||||
facebook: '',
|
||||
google: '',
|
||||
linkedin: '',
|
||||
instagram: '',
|
||||
quora: ''
|
||||
})
|
||||
|
||||
const handleClickShowPassword = () => setFormData(show => ({ ...show, isPasswordShown: !show.isPasswordShown }))
|
||||
|
||||
const handleClickShowConfirmPassword = () =>
|
||||
setFormData(show => ({ ...show, setIsConfirmPasswordShown: !show.setIsConfirmPasswordShown }))
|
||||
|
||||
const handleTabChange = (event: SyntheticEvent, newValue: string) => {
|
||||
setValue(newValue)
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
setFormData({
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
country: '',
|
||||
language: [],
|
||||
date: null,
|
||||
phoneNumber: '',
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
isPasswordShown: false,
|
||||
confirmPassword: '',
|
||||
setIsConfirmPasswordShown: false,
|
||||
twitter: '',
|
||||
facebook: '',
|
||||
google: '',
|
||||
linkedin: '',
|
||||
instagram: '',
|
||||
quora: ''
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<TabContext value={value}>
|
||||
<TabList variant='scrollable' onChange={handleTabChange} className='border-be'>
|
||||
<Tab label='Personal Info' value='personal_info' />
|
||||
<Tab label='Account Details' value='account_details' />
|
||||
<Tab label='Social Links' value='social_links' />
|
||||
</TabList>
|
||||
<form onSubmit={e => e.preventDefault()}>
|
||||
<CardContent>
|
||||
<TabPanel value='personal_info'>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='First Name'
|
||||
placeholder='John'
|
||||
value={formData.firstName}
|
||||
onChange={e => setFormData({ ...formData, firstName: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Last Name'
|
||||
placeholder='Doe'
|
||||
value={formData.lastName}
|
||||
onChange={e => setFormData({ ...formData, lastName: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='Country'
|
||||
value={formData.country}
|
||||
onChange={e => setFormData({ ...formData, country: e.target.value })}
|
||||
>
|
||||
<MenuItem value=''>Select Country</MenuItem>
|
||||
<MenuItem value='UK'>UK</MenuItem>
|
||||
<MenuItem value='USA'>USA</MenuItem>
|
||||
<MenuItem value='Australia'>Australia</MenuItem>
|
||||
<MenuItem value='Germany'>Germany</MenuItem>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='Language'
|
||||
value={formData.language}
|
||||
slotProps={{
|
||||
select: {
|
||||
multiple: true,
|
||||
onChange: e => setFormData({ ...formData, language: e.target.value as string[] })
|
||||
}
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<AppReactDatepicker
|
||||
selected={formData.date}
|
||||
showYearDropdown
|
||||
showMonthDropdown
|
||||
onChange={(date: Date | null) => setFormData({ ...formData, date })}
|
||||
placeholderText='MM/DD/YYYY'
|
||||
customInput={<CustomTextField fullWidth label='Birth Date' placeholder='MM-DD-YYYY' />}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Phone Number'
|
||||
type='number'
|
||||
placeholder='123-456-7890'
|
||||
value={formData.phoneNumber}
|
||||
onChange={e => setFormData({ ...formData, phoneNumber: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</TabPanel>
|
||||
<TabPanel value='account_details'>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Username'
|
||||
placeholder='johnDoe'
|
||||
value={formData.username}
|
||||
onChange={e => setFormData({ ...formData, username: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
type='email'
|
||||
label='Email'
|
||||
placeholder='johndoe@gmail.com'
|
||||
value={formData.email}
|
||||
onChange={e => setFormData({ ...formData, email: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Password'
|
||||
placeholder='············'
|
||||
id='form-layout-tabs-password'
|
||||
type={formData.isPasswordShown ? 'text' : 'password'}
|
||||
value={formData.password}
|
||||
onChange={e => setFormData({ ...formData, password: e.target.value })}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle password visibility'
|
||||
>
|
||||
<i className={formData.isPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Confirm Password'
|
||||
placeholder='············'
|
||||
id='form-layout-tabs-confirm-password'
|
||||
type={formData.setIsConfirmPasswordShown ? 'text' : 'password'}
|
||||
value={formData.confirmPassword}
|
||||
onChange={e => setFormData({ ...formData, confirmPassword: e.target.value })}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowConfirmPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle password visibility'
|
||||
>
|
||||
<i className={formData.setIsConfirmPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</TabPanel>
|
||||
<TabPanel value='social_links'>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Twitter'
|
||||
placeholder='https://twitter.com/johndoe'
|
||||
value={formData.twitter}
|
||||
onChange={e => setFormData({ ...formData, twitter: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Facebook'
|
||||
placeholder='https://facebook.com/johndoe'
|
||||
value={formData.facebook}
|
||||
onChange={e => setFormData({ ...formData, facebook: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Google+'
|
||||
placeholder='https://plus.google.com/johndoe'
|
||||
value={formData.google}
|
||||
onChange={e => setFormData({ ...formData, google: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='LinkedIn'
|
||||
placeholder='https://linkedin.com/johndoe'
|
||||
value={formData.linkedin}
|
||||
onChange={e => setFormData({ ...formData, linkedin: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Instagram'
|
||||
placeholder='https://instagram.com/johndoe'
|
||||
value={formData.instagram}
|
||||
onChange={e => setFormData({ ...formData, instagram: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Quora'
|
||||
placeholder='https://quora.com/johndoe'
|
||||
value={formData.quora}
|
||||
onChange={e => setFormData({ ...formData, quora: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</TabPanel>
|
||||
</CardContent>
|
||||
<Divider />
|
||||
<CardActions>
|
||||
<Button type='submit' variant='contained' className='mie-2'>
|
||||
Submit
|
||||
</Button>
|
||||
<Button type='reset' variant='tonal' color='secondary' onClick={() => handleReset()}>
|
||||
Reset
|
||||
</Button>
|
||||
</CardActions>
|
||||
</form>
|
||||
</TabContext>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormLayoutsWithTabs
|
||||
@@ -0,0 +1,166 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Button from '@mui/material/Button'
|
||||
import CardHeader from '@mui/material/CardHeader'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import CircularProgress from '@mui/material/CircularProgress'
|
||||
import InputAdornment from '@mui/material/InputAdornment'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
|
||||
// Third-party Imports
|
||||
import { toast } from 'react-toastify'
|
||||
import { useForm, Controller } from 'react-hook-form'
|
||||
|
||||
// Components Imports
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
type FormValues = {
|
||||
firstName: string
|
||||
lastName: string
|
||||
email: string
|
||||
password: string
|
||||
}
|
||||
|
||||
const FormValidationAsyncSubmit = () => {
|
||||
// States
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [isPasswordShown, setIsPasswordShown] = useState(false)
|
||||
|
||||
// Hooks
|
||||
const {
|
||||
control,
|
||||
reset,
|
||||
handleSubmit,
|
||||
formState: { errors }
|
||||
} = useForm<FormValues>({
|
||||
defaultValues: {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
email: '',
|
||||
password: ''
|
||||
}
|
||||
})
|
||||
|
||||
const handleClickShowPassword = () => setIsPasswordShown(show => !show)
|
||||
|
||||
const onSubmit = async () => {
|
||||
setLoading(true)
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
||||
|
||||
await sleep(2000)
|
||||
setLoading(false)
|
||||
toast.success('Form Submitted')
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Async Submit' />
|
||||
<CardContent>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Controller
|
||||
name='firstName'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='First Name'
|
||||
placeholder='John'
|
||||
{...(errors.firstName && { error: true, helperText: 'This field is required.' })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Controller
|
||||
name='lastName'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='Last Name'
|
||||
placeholder='Doe'
|
||||
{...(errors.lastName && { error: true, helperText: 'This field is required.' })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Controller
|
||||
name='email'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
type='email'
|
||||
label='Email'
|
||||
placeholder='johndoe@gmail.com'
|
||||
{...(errors.email && { error: true, helperText: 'This field is required.' })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Controller
|
||||
name='password'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='Password'
|
||||
id='outlined-password'
|
||||
placeholder='············'
|
||||
type={isPasswordShown ? 'text' : 'password'}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle password visibility'
|
||||
>
|
||||
<i className={isPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
{...(errors.password && { error: true, helperText: 'This field is required' })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }} className='flex gap-4'>
|
||||
<Button variant='contained' type='submit' className='gap-2'>
|
||||
{loading && <CircularProgress size={20} color='inherit' />}
|
||||
Submit
|
||||
</Button>
|
||||
<Button variant='tonal' color='secondary' type='reset' onClick={() => reset()}>
|
||||
Reset
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormValidationAsyncSubmit
|
||||
@@ -0,0 +1,267 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import CardHeader from '@mui/material/CardHeader'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Radio from '@mui/material/Radio'
|
||||
import RadioGroup from '@mui/material/RadioGroup'
|
||||
import FormControlLabel from '@mui/material/FormControlLabel'
|
||||
import Button from '@mui/material/Button'
|
||||
import Checkbox from '@mui/material/Checkbox'
|
||||
import FormLabel from '@mui/material/FormLabel'
|
||||
import FormHelperText from '@mui/material/FormHelperText'
|
||||
import MenuItem from '@mui/material/MenuItem'
|
||||
import FormControl from '@mui/material/FormControl'
|
||||
import InputAdornment from '@mui/material/InputAdornment'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
|
||||
// Third-party Imports
|
||||
import { toast } from 'react-toastify'
|
||||
import { useForm, Controller } from 'react-hook-form'
|
||||
|
||||
// Components Imports
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
// Styled Component Imports
|
||||
import AppReactDatepicker from '@/libs/styles/AppReactDatepicker'
|
||||
|
||||
type FormValues = {
|
||||
firstName: string
|
||||
lastName: string
|
||||
email: string
|
||||
password: string
|
||||
dob: Date | null | undefined
|
||||
select: string
|
||||
textarea: string
|
||||
radio: boolean
|
||||
checkbox: boolean
|
||||
}
|
||||
|
||||
const FormValidationBasic = () => {
|
||||
// States
|
||||
const [isPasswordShown, setIsPasswordShown] = useState(false)
|
||||
|
||||
// Hooks
|
||||
const {
|
||||
control,
|
||||
reset,
|
||||
handleSubmit,
|
||||
formState: { errors }
|
||||
} = useForm<FormValues>({
|
||||
defaultValues: {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
dob: null,
|
||||
select: '',
|
||||
textarea: '',
|
||||
radio: false,
|
||||
checkbox: false
|
||||
}
|
||||
})
|
||||
|
||||
const handleClickShowPassword = () => setIsPasswordShown(show => !show)
|
||||
|
||||
const onSubmit = () => toast.success('Form Submitted')
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Basic' />
|
||||
<CardContent>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='firstName'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='First Name'
|
||||
placeholder='John'
|
||||
{...(errors.firstName && { error: true, helperText: 'This field is required.' })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='lastName'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='Last Name'
|
||||
placeholder='Doe'
|
||||
{...(errors.lastName && { error: true, helperText: 'This field is required.' })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='email'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
type='email'
|
||||
label='Email'
|
||||
placeholder='johndoe@gmail.com'
|
||||
{...(errors.email && { error: true, helperText: 'This field is required.' })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='password'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='Password'
|
||||
placeholder='············'
|
||||
id='form-validation-basic-password'
|
||||
type={isPasswordShown ? 'text' : 'password'}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle password visibility'
|
||||
>
|
||||
<i className={isPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
{...(errors.password && { error: true, helperText: 'This field is required.' })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='dob'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<AppReactDatepicker
|
||||
selected={value}
|
||||
showYearDropdown
|
||||
showMonthDropdown
|
||||
onChange={onChange}
|
||||
placeholderText='MM/DD/YYYY'
|
||||
customInput={
|
||||
<CustomTextField
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
fullWidth
|
||||
label='Date Of Birth'
|
||||
{...(errors.dob && { error: true, helperText: 'This field is required.' })}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='select'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField select fullWidth label='Country' {...field} error={Boolean(errors.select)}>
|
||||
<MenuItem value=''>Select Country</MenuItem>
|
||||
<MenuItem value='UK'>UK</MenuItem>
|
||||
<MenuItem value='USA'>USA</MenuItem>
|
||||
<MenuItem value='Australia'>Australia</MenuItem>
|
||||
<MenuItem value='Germany'>Germany</MenuItem>
|
||||
</CustomTextField>
|
||||
)}
|
||||
/>
|
||||
{errors.select && <FormHelperText error>This field is required.</FormHelperText>}
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Controller
|
||||
name='textarea'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
rows={4}
|
||||
fullWidth
|
||||
multiline
|
||||
label='Bio'
|
||||
{...(errors.textarea && { error: true, helperText: 'This field is required.' })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<FormControl error={Boolean(errors.radio)}>
|
||||
<FormLabel>Gender</FormLabel>
|
||||
<Controller
|
||||
name='radio'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<RadioGroup row {...field} name='radio-buttons-group'>
|
||||
<FormControlLabel value='female' control={<Radio />} label='Female' />
|
||||
<FormControlLabel value='male' control={<Radio />} label='Male' />
|
||||
<FormControlLabel value='other' control={<Radio />} label='Other' />
|
||||
</RadioGroup>
|
||||
)}
|
||||
/>
|
||||
{errors.radio && <FormHelperText error>This field is required.</FormHelperText>}
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<FormControl error={Boolean(errors.checkbox)}>
|
||||
<Controller
|
||||
name='checkbox'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel control={<Checkbox {...field} />} label='Agree to our terms and conditions' />
|
||||
)}
|
||||
/>
|
||||
{errors.checkbox && <FormHelperText error>This field is required.</FormHelperText>}
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }} className='flex gap-4'>
|
||||
<Button variant='contained' type='submit'>
|
||||
Submit
|
||||
</Button>
|
||||
<Button variant='tonal' color='secondary' type='reset' onClick={() => reset()}>
|
||||
Reset
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormValidationBasic
|
||||
@@ -0,0 +1,174 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Button from '@mui/material/Button'
|
||||
import CardHeader from '@mui/material/CardHeader'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import InputAdornment from '@mui/material/InputAdornment'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
|
||||
// Third-party Imports
|
||||
import { toast } from 'react-toastify'
|
||||
import { Controller, useForm } from 'react-hook-form'
|
||||
import { valibotResolver } from '@hookform/resolvers/valibot'
|
||||
import { email, object, minLength, string, pipe, nonEmpty } from 'valibot'
|
||||
import type { InferInput } from 'valibot'
|
||||
|
||||
// Components Imports
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
type FormData = InferInput<typeof schema>
|
||||
|
||||
const schema = object({
|
||||
firstName: pipe(
|
||||
string(),
|
||||
nonEmpty('This field is required'),
|
||||
minLength(3, 'First Name must be at least 3 characters long')
|
||||
),
|
||||
lastName: pipe(
|
||||
string(),
|
||||
nonEmpty('This field is required'),
|
||||
minLength(3, 'Last Name must be at least 3 characters long')
|
||||
),
|
||||
email: pipe(string(), minLength(1, 'This field is required'), email('Please enter a valid email address')),
|
||||
password: pipe(
|
||||
string(),
|
||||
nonEmpty('This field is required'),
|
||||
minLength(8, 'Password must be at least 8 characters long')
|
||||
)
|
||||
})
|
||||
|
||||
const FormValidationOnScheme = () => {
|
||||
// States
|
||||
const [isPasswordShown, setIsPasswordShown] = useState(false)
|
||||
|
||||
// Hooks
|
||||
const {
|
||||
control,
|
||||
reset,
|
||||
handleSubmit,
|
||||
formState: { errors }
|
||||
} = useForm<FormData>({
|
||||
resolver: valibotResolver(schema),
|
||||
defaultValues: {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
email: '',
|
||||
password: ''
|
||||
}
|
||||
})
|
||||
|
||||
const handleClickShowPassword = () => setIsPasswordShown(show => !show)
|
||||
|
||||
const onSubmit = () => toast.success('Form Submitted')
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Validation Schema With OnChange' />
|
||||
<CardContent>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Controller
|
||||
name='firstName'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='First Name'
|
||||
placeholder='John'
|
||||
{...(errors.firstName && { error: true, helperText: errors.firstName.message })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Controller
|
||||
name='lastName'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='Last Name'
|
||||
placeholder='Doe'
|
||||
{...(errors.lastName && { error: true, helperText: errors.lastName.message })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Controller
|
||||
name='email'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
type='email'
|
||||
label='Email'
|
||||
placeholder='johndoe@gmail.com'
|
||||
{...(errors.email && { error: true, helperText: errors.email.message })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Controller
|
||||
name='password'
|
||||
control={control}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='Password'
|
||||
placeholder='············'
|
||||
id='form-validation-scheme-password'
|
||||
type={isPasswordShown ? 'text' : 'password'}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle password visibility'
|
||||
>
|
||||
<i className={isPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
{...(errors.password && { error: true, helperText: errors.password.message })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }} className='flex gap-4'>
|
||||
<Button variant='contained' type='submit'>
|
||||
Submit
|
||||
</Button>
|
||||
<Button variant='tonal' color='secondary' type='reset' onClick={() => reset()}>
|
||||
Reset
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormValidationOnScheme
|
||||
@@ -0,0 +1,387 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import Stepper from '@mui/material/Stepper'
|
||||
import Step from '@mui/material/Step'
|
||||
import StepLabel from '@mui/material/StepLabel'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Button from '@mui/material/Button'
|
||||
import MenuItem from '@mui/material/MenuItem'
|
||||
import InputAdornment from '@mui/material/InputAdornment'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
|
||||
// Third-party Imports
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
// Component Imports
|
||||
import DirectionalIcon from '@components/DirectionalIcon'
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
// Styled Component Imports
|
||||
import StepperWrapper from '@core/styles/stepper'
|
||||
import StepperCustomDot from '@components/stepper-dot'
|
||||
|
||||
type FormDataType = {
|
||||
username: string
|
||||
email: string
|
||||
password: string
|
||||
isPasswordShown: boolean
|
||||
confirmPassword: string
|
||||
isConfirmPasswordShown: boolean
|
||||
firstName: string
|
||||
lastName: string
|
||||
country: string
|
||||
language: string[]
|
||||
twitter: string
|
||||
facebook: string
|
||||
instagram: string
|
||||
github: string
|
||||
}
|
||||
|
||||
// Vars
|
||||
const steps = [
|
||||
{
|
||||
title: 'Account Details',
|
||||
subtitle: 'Enter your account details'
|
||||
},
|
||||
{
|
||||
title: 'Personal Info',
|
||||
subtitle: 'Setup Information'
|
||||
},
|
||||
{
|
||||
title: 'Social Links',
|
||||
subtitle: 'Add Social Links'
|
||||
}
|
||||
]
|
||||
|
||||
const StepperAlternativeLabel = () => {
|
||||
// States
|
||||
const [activeStep, setActiveStep] = useState(0)
|
||||
|
||||
const [formData, setFormData] = useState<FormDataType>({
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
isPasswordShown: false,
|
||||
confirmPassword: '',
|
||||
isConfirmPasswordShown: false,
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
country: '',
|
||||
language: [],
|
||||
twitter: '',
|
||||
facebook: '',
|
||||
instagram: '',
|
||||
github: ''
|
||||
})
|
||||
|
||||
const handleClickShowPassword = () => setFormData(show => ({ ...show, isPasswordShown: !show.isPasswordShown }))
|
||||
|
||||
const handleClickShowConfirmPassword = () =>
|
||||
setFormData(show => ({ ...show, isConfirmPasswordShown: !show.isConfirmPasswordShown }))
|
||||
|
||||
const handleReset = () => {
|
||||
setActiveStep(0)
|
||||
setFormData({
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
isPasswordShown: false,
|
||||
confirmPassword: '',
|
||||
isConfirmPasswordShown: false,
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
country: '',
|
||||
language: [],
|
||||
twitter: '',
|
||||
facebook: '',
|
||||
instagram: '',
|
||||
github: ''
|
||||
})
|
||||
}
|
||||
|
||||
const handleNext = () => {
|
||||
setActiveStep(prevActiveStep => prevActiveStep + 1)
|
||||
|
||||
if (activeStep === steps.length - 1) {
|
||||
toast.success('Form Submitted')
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
setActiveStep(prevActiveStep => prevActiveStep - 1)
|
||||
}
|
||||
|
||||
const renderStepContent = (activeStep: number) => {
|
||||
switch (activeStep) {
|
||||
case 0:
|
||||
return (
|
||||
<>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Username'
|
||||
placeholder='johnDoe'
|
||||
value={formData.username}
|
||||
onChange={e => setFormData({ ...formData, username: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
type='email'
|
||||
label='Email'
|
||||
placeholder='johndoe@gmail.com'
|
||||
value={formData.email}
|
||||
onChange={e => setFormData({ ...formData, email: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Password'
|
||||
placeholder='············'
|
||||
id='stepper-alternative-password'
|
||||
type={formData.isPasswordShown ? 'text' : 'password'}
|
||||
value={formData.password}
|
||||
onChange={e => setFormData({ ...formData, password: e.target.value })}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle password visibility'
|
||||
>
|
||||
<i className={formData.isPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Confirm Password'
|
||||
placeholder='············'
|
||||
id='stepper-alternative-confirm-password'
|
||||
type={formData.isConfirmPasswordShown ? 'text' : 'password'}
|
||||
value={formData.confirmPassword}
|
||||
onChange={e => setFormData({ ...formData, confirmPassword: e.target.value })}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowConfirmPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle confirm password visibility'
|
||||
>
|
||||
<i className={formData.isConfirmPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</>
|
||||
)
|
||||
case 1:
|
||||
return (
|
||||
<>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='First Name'
|
||||
placeholder='John'
|
||||
value={formData.firstName}
|
||||
onChange={e => setFormData({ ...formData, firstName: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Last Name'
|
||||
placeholder='Doe'
|
||||
value={formData.lastName}
|
||||
onChange={e => setFormData({ ...formData, lastName: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='Country'
|
||||
value={formData.country}
|
||||
onChange={e => setFormData({ ...formData, country: e.target.value as string })}
|
||||
>
|
||||
<MenuItem value=''>Select Country</MenuItem>
|
||||
<MenuItem value='UK'>UK</MenuItem>
|
||||
<MenuItem value='USA'>USA</MenuItem>
|
||||
<MenuItem value='Australia'>Australia</MenuItem>
|
||||
<MenuItem value='Germany'>Germany</MenuItem>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='Language'
|
||||
value={formData.language}
|
||||
slotProps={{
|
||||
select: {
|
||||
multiple: true,
|
||||
onChange: e => setFormData({ ...formData, language: e.target.value as string[] })
|
||||
}
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
</>
|
||||
)
|
||||
case 2:
|
||||
return (
|
||||
<>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Facebook'
|
||||
placeholder='https://www.facebook.com/johndoe'
|
||||
value={formData.facebook}
|
||||
onChange={e => setFormData({ ...formData, facebook: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Twitter'
|
||||
placeholder='https://www.twitter.com/johndoe'
|
||||
value={formData.twitter}
|
||||
onChange={e => setFormData({ ...formData, twitter: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Instagram'
|
||||
placeholder='https://www.instagram.com/johndoe'
|
||||
value={formData.instagram}
|
||||
onChange={e => setFormData({ ...formData, instagram: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Github'
|
||||
placeholder='https://www.github.com/johndoe'
|
||||
value={formData.github}
|
||||
onChange={e => setFormData({ ...formData, github: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
</>
|
||||
)
|
||||
default:
|
||||
return 'Unknown step'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<StepperWrapper>
|
||||
<Stepper activeStep={activeStep} alternativeLabel>
|
||||
{steps.map(label => {
|
||||
return (
|
||||
<Step key={label.title}>
|
||||
<StepLabel
|
||||
slots={{
|
||||
stepIcon: StepperCustomDot
|
||||
}}
|
||||
>
|
||||
<div className='step-label'>
|
||||
<div>
|
||||
<Typography className='step-title'>{label.title}</Typography>
|
||||
<Typography className='step-subtitle'>{label.subtitle}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</StepLabel>
|
||||
</Step>
|
||||
)
|
||||
})}
|
||||
</Stepper>
|
||||
</StepperWrapper>
|
||||
<Card className='mt-4'>
|
||||
<CardContent>
|
||||
{activeStep === steps.length ? (
|
||||
<>
|
||||
<Typography className='mlb-2 mli-1'>All steps are completed!</Typography>
|
||||
<div className='flex justify-end mt-4'>
|
||||
<Button variant='contained' onClick={handleReset}>
|
||||
Reset
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<form onSubmit={e => e.preventDefault()}>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
{steps[activeStep].title}
|
||||
</Typography>
|
||||
<Typography variant='body2'>{steps[activeStep].subtitle}</Typography>
|
||||
</Grid>
|
||||
{renderStepContent(activeStep)}
|
||||
<Grid size={{ xs: 12 }} className='flex justify-between'>
|
||||
<Button
|
||||
variant='tonal'
|
||||
disabled={activeStep === 0}
|
||||
onClick={handleBack}
|
||||
color='secondary'
|
||||
startIcon={<DirectionalIcon ltrIconClass='tabler-arrow-left' rtlIconClass='tabler-arrow-right' />}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Button
|
||||
variant='contained'
|
||||
onClick={handleNext}
|
||||
endIcon={
|
||||
activeStep === steps.length - 1 ? (
|
||||
<i className='tabler-check' />
|
||||
) : (
|
||||
<DirectionalIcon ltrIconClass='tabler-arrow-right' rtlIconClass='tabler-arrow-left' />
|
||||
)
|
||||
}
|
||||
>
|
||||
{activeStep === steps.length - 1 ? 'Submit' : 'Next'}
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default StepperAlternativeLabel
|
||||
@@ -0,0 +1,440 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import { styled, useTheme } from '@mui/material/styles'
|
||||
import useMediaQuery from '@mui/material/useMediaQuery'
|
||||
import Card from '@mui/material/Card'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import Stepper from '@mui/material/Stepper'
|
||||
import StepLabel from '@mui/material/StepLabel'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Button from '@mui/material/Button'
|
||||
import MenuItem from '@mui/material/MenuItem'
|
||||
import InputAdornment from '@mui/material/InputAdornment'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import Divider from '@mui/material/Divider'
|
||||
import MuiStep from '@mui/material/Step'
|
||||
import type { StepProps } from '@mui/material/Step'
|
||||
|
||||
// Third-party Imports
|
||||
import { toast } from 'react-toastify'
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Component Imports
|
||||
import CustomAvatar from '@core/components/mui/Avatar'
|
||||
import DirectionalIcon from '@components/DirectionalIcon'
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
// Styles Component Imports
|
||||
import StepperWrapper from '@core/styles/stepper'
|
||||
|
||||
type FormDataType = {
|
||||
username: string
|
||||
email: string
|
||||
password: string
|
||||
isPasswordShown: boolean
|
||||
confirmPassword: string
|
||||
isConfirmPasswordShown: boolean
|
||||
firstName: string
|
||||
lastName: string
|
||||
country: string
|
||||
language: string[]
|
||||
twitter: string
|
||||
facebook: string
|
||||
instagram: string
|
||||
github: string
|
||||
}
|
||||
|
||||
// Vars
|
||||
const steps = [
|
||||
{
|
||||
icon: 'tabler-file-analytics',
|
||||
title: 'Account Details',
|
||||
subtitle: 'Enter your account details'
|
||||
},
|
||||
{
|
||||
icon: 'tabler-user',
|
||||
title: 'Personal Info',
|
||||
subtitle: 'Setup Information'
|
||||
},
|
||||
{
|
||||
icon: 'tabler-brand-instagram',
|
||||
title: 'Social Links',
|
||||
subtitle: 'Add Social Links'
|
||||
}
|
||||
]
|
||||
|
||||
const Step = styled(MuiStep)<StepProps>(({ theme }) => ({
|
||||
paddingInline: theme.spacing(7),
|
||||
'&:first-of-type': {
|
||||
paddingLeft: 0
|
||||
},
|
||||
'&:last-of-type': {
|
||||
paddingRight: 0
|
||||
},
|
||||
'& .MuiStepLabel-iconContainer': {
|
||||
display: 'none'
|
||||
},
|
||||
'&.Mui-completed .step-title , &.Mui-completed .step-subtitle': {
|
||||
color: 'var(--mui-palette-text-disabled)'
|
||||
},
|
||||
|
||||
[theme.breakpoints.down('md')]: {
|
||||
padding: 0,
|
||||
':not(:last-of-type)': {
|
||||
marginBlockEnd: theme.spacing(6)
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
const StepperCustomHorizontal = () => {
|
||||
// States
|
||||
const [activeStep, setActiveStep] = useState(0)
|
||||
|
||||
const theme = useTheme()
|
||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'))
|
||||
|
||||
const [formData, setFormData] = useState<FormDataType>({
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
isPasswordShown: false,
|
||||
confirmPassword: '',
|
||||
isConfirmPasswordShown: false,
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
country: '',
|
||||
language: [],
|
||||
twitter: '',
|
||||
facebook: '',
|
||||
instagram: '',
|
||||
github: ''
|
||||
})
|
||||
|
||||
const handleClickShowPassword = () => setFormData(show => ({ ...show, isPasswordShown: !show.isPasswordShown }))
|
||||
|
||||
const handleClickShowConfirmPassword = () =>
|
||||
setFormData(show => ({ ...show, isConfirmPasswordShown: !show.isConfirmPasswordShown }))
|
||||
|
||||
const handleReset = () => {
|
||||
setActiveStep(0)
|
||||
setFormData({
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
isPasswordShown: false,
|
||||
confirmPassword: '',
|
||||
isConfirmPasswordShown: false,
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
country: '',
|
||||
language: [],
|
||||
twitter: '',
|
||||
facebook: '',
|
||||
instagram: '',
|
||||
github: ''
|
||||
})
|
||||
}
|
||||
|
||||
const handleNext = () => {
|
||||
setActiveStep(prevActiveStep => prevActiveStep + 1)
|
||||
|
||||
if (activeStep === steps.length - 1) {
|
||||
toast.success('Form Submitted')
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
setActiveStep(prevActiveStep => prevActiveStep - 1)
|
||||
}
|
||||
|
||||
const renderStepContent = (activeStep: number) => {
|
||||
switch (activeStep) {
|
||||
case 0:
|
||||
return (
|
||||
<>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Username'
|
||||
placeholder='johnDoe'
|
||||
value={formData.username}
|
||||
onChange={e => setFormData({ ...formData, username: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
type='email'
|
||||
label='Email'
|
||||
placeholder='johndoe@gmail.com'
|
||||
value={formData.email}
|
||||
onChange={e => setFormData({ ...formData, email: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Password'
|
||||
placeholder='············'
|
||||
id='stepper-customHorizontal-password'
|
||||
type={formData.isPasswordShown ? 'text' : 'password'}
|
||||
value={formData.password}
|
||||
onChange={e => setFormData({ ...formData, password: e.target.value })}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle password visibility'
|
||||
>
|
||||
<i className={formData.isPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Confirm Password'
|
||||
placeholder='············'
|
||||
id='stepper-customHorizontal-confirm-password'
|
||||
type={formData.isConfirmPasswordShown ? 'text' : 'password'}
|
||||
value={formData.confirmPassword}
|
||||
onChange={e => setFormData({ ...formData, confirmPassword: e.target.value })}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowConfirmPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle confirm password visibility'
|
||||
>
|
||||
<i className={formData.isConfirmPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</>
|
||||
)
|
||||
case 1:
|
||||
return (
|
||||
<>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='First Name'
|
||||
placeholder='John'
|
||||
value={formData.firstName}
|
||||
onChange={e => setFormData({ ...formData, firstName: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Last Name'
|
||||
placeholder='Doe'
|
||||
value={formData.lastName}
|
||||
onChange={e => setFormData({ ...formData, lastName: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='Country'
|
||||
value={formData.country}
|
||||
onChange={e => setFormData({ ...formData, country: e.target.value as string })}
|
||||
>
|
||||
<MenuItem value=''>Select Country</MenuItem>
|
||||
<MenuItem value='UK'>UK</MenuItem>
|
||||
<MenuItem value='USA'>USA</MenuItem>
|
||||
<MenuItem value='Australia'>Australia</MenuItem>
|
||||
<MenuItem value='Germany'>Germany</MenuItem>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='Language'
|
||||
value={formData.language}
|
||||
slotProps={{
|
||||
select: {
|
||||
multiple: true,
|
||||
onChange: e => setFormData({ ...formData, language: e.target.value as string[] })
|
||||
}
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
</>
|
||||
)
|
||||
case 2:
|
||||
return (
|
||||
<>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Facebook'
|
||||
placeholder='https://www.facebook.com/johndoe'
|
||||
value={formData.facebook}
|
||||
onChange={e => setFormData({ ...formData, facebook: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Twitter'
|
||||
placeholder='https://www.twitter.com/johndoe'
|
||||
value={formData.twitter}
|
||||
onChange={e => setFormData({ ...formData, twitter: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Instagram'
|
||||
placeholder='https://www.instagram.com/johndoe'
|
||||
value={formData.instagram}
|
||||
onChange={e => setFormData({ ...formData, instagram: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Github'
|
||||
placeholder='https://www.github.com/johndoe'
|
||||
value={formData.github}
|
||||
onChange={e => setFormData({ ...formData, github: e.target.value })}
|
||||
/>
|
||||
</Grid>
|
||||
</>
|
||||
)
|
||||
default:
|
||||
return 'Unknown step'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<StepperWrapper>
|
||||
<Stepper
|
||||
activeStep={activeStep}
|
||||
connector={
|
||||
!isSmallScreen ? (
|
||||
<DirectionalIcon
|
||||
ltrIconClass='tabler-chevron-right'
|
||||
rtlIconClass='tabler-chevron-left'
|
||||
className='text-xl'
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
>
|
||||
{steps.map((step, index) => {
|
||||
return (
|
||||
<Step key={index}>
|
||||
<StepLabel>
|
||||
<div className='step-label'>
|
||||
<CustomAvatar
|
||||
variant='rounded'
|
||||
skin={activeStep === index ? 'filled' : 'light'}
|
||||
{...(activeStep >= index && { color: 'primary' })}
|
||||
{...(activeStep === index && { className: 'shadow-primarySm' })}
|
||||
size={38}
|
||||
>
|
||||
<i className={classnames(step.icon)} />
|
||||
</CustomAvatar>
|
||||
<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 !important' }} />
|
||||
<CardContent>
|
||||
{activeStep === steps.length ? (
|
||||
<>
|
||||
<Typography className='mlb-2 mli-1'>All steps are completed!</Typography>
|
||||
<div className='flex justify-end mt-4'>
|
||||
<Button variant='contained' onClick={handleReset}>
|
||||
Reset
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<form onSubmit={e => e.preventDefault()}>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
{steps[activeStep].title}
|
||||
</Typography>
|
||||
<Typography variant='body2'>{steps[activeStep].subtitle}</Typography>
|
||||
</Grid>
|
||||
{renderStepContent(activeStep)}
|
||||
<Grid size={{ xs: 12 }} className='flex justify-between'>
|
||||
<Button
|
||||
variant='tonal'
|
||||
disabled={activeStep === 0}
|
||||
onClick={handleBack}
|
||||
startIcon={<DirectionalIcon ltrIconClass='tabler-arrow-left' rtlIconClass='tabler-arrow-right' />}
|
||||
color='secondary'
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Button
|
||||
variant='contained'
|
||||
onClick={handleNext}
|
||||
endIcon={
|
||||
activeStep === steps.length - 1 ? (
|
||||
<i className='tabler-check' />
|
||||
) : (
|
||||
<DirectionalIcon ltrIconClass='tabler-arrow-right' rtlIconClass='tabler-arrow-left' />
|
||||
)
|
||||
}
|
||||
>
|
||||
{activeStep === steps.length - 1 ? 'Submit' : 'Next'}
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default StepperCustomHorizontal
|
||||
@@ -0,0 +1,451 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import type { ChangeEvent } from 'react'
|
||||
import { Fragment, useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import { styled } from '@mui/material/styles'
|
||||
import Box from '@mui/material/Box'
|
||||
import Card from '@mui/material/Card'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Button from '@mui/material/Button'
|
||||
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 Typography from '@mui/material/Typography'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import InputAdornment from '@mui/material/InputAdornment'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import MuiStep from '@mui/material/Step'
|
||||
import type { StepProps } from '@mui/material/Step'
|
||||
import type { SelectChangeEvent } from '@mui/material/Select'
|
||||
import type { CardContentProps } from '@mui/material/CardContent'
|
||||
|
||||
// Third Party Imports
|
||||
import { toast } from 'react-toastify'
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Components Imports
|
||||
import CustomAvatar from '@core/components/mui/Avatar'
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
import DirectionalIcon from '@components/DirectionalIcon'
|
||||
|
||||
// Styled Component Imports
|
||||
import StepperWrapper from '@core/styles/stepper'
|
||||
|
||||
interface State {
|
||||
password: string
|
||||
password2: string
|
||||
showPassword: boolean
|
||||
showPassword2: boolean
|
||||
}
|
||||
|
||||
const steps = [
|
||||
{
|
||||
icon: 'tabler-file-analytics',
|
||||
title: 'Account Details',
|
||||
subtitle: 'Enter your Account Details'
|
||||
},
|
||||
{
|
||||
icon: 'tabler-user',
|
||||
title: 'Personal Info',
|
||||
subtitle: 'Setup Information'
|
||||
},
|
||||
{
|
||||
icon: 'tabler-brand-instagram',
|
||||
title: 'Social Links',
|
||||
subtitle: 'Add Social Links'
|
||||
}
|
||||
]
|
||||
|
||||
const StepperHeaderContainer = styled(CardContent)<CardContentProps>(({ theme }) => ({
|
||||
borderRight: `1px solid 'var(--mui-palette-divider)'`,
|
||||
[theme.breakpoints.down('md')]: {
|
||||
borderRight: 0,
|
||||
borderBottom: `1px solid 'var(--mui-palette-divider)'`
|
||||
}
|
||||
}))
|
||||
|
||||
const Step = styled(MuiStep)<StepProps>(({ theme }) => ({
|
||||
'& .MuiStepLabel-root': {
|
||||
paddingTop: 0
|
||||
},
|
||||
'&:first-of-type .MuiStepLabel-root': {
|
||||
paddingTop: theme.spacing(1)
|
||||
},
|
||||
'&:not(:last-of-type) .MuiStepLabel-root': {
|
||||
paddingBottom: theme.spacing(6)
|
||||
},
|
||||
'&:last-of-type .MuiStepLabel-root': {
|
||||
paddingBottom: theme.spacing(1)
|
||||
},
|
||||
'& .MuiStepLabel-iconContainer': {
|
||||
display: 'none'
|
||||
},
|
||||
'&.Mui-completed .step-title , &.Mui-completed .step-subtitle': {
|
||||
color: 'var(--mui-palette-text-disabled)'
|
||||
}
|
||||
}))
|
||||
|
||||
const StepperCustomVertical = () => {
|
||||
// States
|
||||
const [email, setEmail] = useState<string>('')
|
||||
const [google, setGoogle] = useState<string>('')
|
||||
const [country, setCountry] = useState<string>('')
|
||||
const [twitter, setTwitter] = useState<string>('')
|
||||
const [username, setUsername] = useState<string>('')
|
||||
const [lastName, setLastName] = useState<string>('')
|
||||
const [facebook, setFacebook] = useState<string>('')
|
||||
const [linkedIn, setLinkedIn] = useState<string>('')
|
||||
const [firstName, setFirstName] = useState<string>('')
|
||||
const [activeStep, setActiveStep] = useState<number>(0)
|
||||
const [language, setLanguage] = useState<string[]>([])
|
||||
|
||||
const [state, setState] = useState<State>({
|
||||
password: '',
|
||||
password2: '',
|
||||
showPassword: false,
|
||||
showPassword2: false
|
||||
})
|
||||
|
||||
// Handle Stepper
|
||||
const handleBack = () => {
|
||||
setActiveStep(prevActiveStep => prevActiveStep - 1)
|
||||
}
|
||||
|
||||
const handleNext = () => {
|
||||
setActiveStep(prevActiveStep => prevActiveStep + 1)
|
||||
|
||||
if (activeStep === steps.length - 1) {
|
||||
toast.success('Form Submitted')
|
||||
}
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
setEmail('')
|
||||
setGoogle('')
|
||||
setCountry('')
|
||||
setTwitter('')
|
||||
setUsername('')
|
||||
setLastName('')
|
||||
setFacebook('')
|
||||
setLinkedIn('')
|
||||
setLanguage([])
|
||||
setFirstName('')
|
||||
setActiveStep(0)
|
||||
setState({ ...state, password: '', password2: '' })
|
||||
}
|
||||
|
||||
// Handle Password
|
||||
const handlePasswordChange = (prop: keyof State) => (event: ChangeEvent<HTMLInputElement>) => {
|
||||
setState({ ...state, [prop]: event.target.value })
|
||||
}
|
||||
|
||||
const handleClickShowPassword = () => {
|
||||
setState({ ...state, showPassword: !state.showPassword })
|
||||
}
|
||||
|
||||
// Handle Confirm Password
|
||||
const handleConfirmChange = (prop: keyof State) => (event: ChangeEvent<HTMLInputElement>) => {
|
||||
setState({ ...state, [prop]: event.target.value })
|
||||
}
|
||||
|
||||
const handleClickShowConfirmPassword = () => {
|
||||
setState({ ...state, showPassword2: !state.showPassword2 })
|
||||
}
|
||||
|
||||
// Handle Language
|
||||
const handleSelectChange = (event: SelectChangeEvent<string[]>) => {
|
||||
setLanguage(event.target.value as string[])
|
||||
}
|
||||
|
||||
const getStepContent = (step: number) => {
|
||||
switch (step) {
|
||||
case 0:
|
||||
return (
|
||||
<Fragment>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Username'
|
||||
value={username}
|
||||
placeholder='JohnDoe'
|
||||
onChange={e => setUsername(e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
type='email'
|
||||
label='Email'
|
||||
value={email}
|
||||
placeholder='johndoe@gmail.com'
|
||||
onChange={e => setEmail(e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Password'
|
||||
placeholder='············'
|
||||
value={state.password}
|
||||
id='stepper-custom-vertical-account-password'
|
||||
onChange={handlePasswordChange('password')}
|
||||
type={state.showPassword ? 'text' : 'password'}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle password visibility'
|
||||
>
|
||||
<i className={state.showPassword ? 'tabler-eye' : 'tabler-eye-off'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
value={state.password2}
|
||||
label='Confirm Password'
|
||||
placeholder='············'
|
||||
id='stepper-custom-vertical-account-password-2'
|
||||
onChange={handleConfirmChange('password2')}
|
||||
type={state.showPassword2 ? 'text' : 'password'}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle password visibility'
|
||||
onClick={handleClickShowConfirmPassword}
|
||||
>
|
||||
<i className={state.showPassword2 ? 'tabler-eye' : 'tabler-eye-off'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)
|
||||
case 1:
|
||||
return (
|
||||
<Fragment key={step}>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
value={firstName}
|
||||
label='First Name'
|
||||
placeholder='John'
|
||||
onChange={e => setFirstName(e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
value={lastName}
|
||||
label='Last Name'
|
||||
placeholder='Doe'
|
||||
onChange={e => setLastName(e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='Country'
|
||||
value={country}
|
||||
onChange={e => setCountry(e.target.value)}
|
||||
id='stepper-custom-vertical-personal-select'
|
||||
>
|
||||
<MenuItem value=''>Select Country</MenuItem>
|
||||
<MenuItem value='UK'>UK</MenuItem>
|
||||
<MenuItem value='USA'>USA</MenuItem>
|
||||
<MenuItem value='Australia'>Australia</MenuItem>
|
||||
<MenuItem value='Germany'>Germany</MenuItem>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='Language'
|
||||
id='stepper-custom-vertical-personal-multiple-select'
|
||||
slotProps={{
|
||||
select: {
|
||||
multiple: true,
|
||||
value: language,
|
||||
onChange: e => handleSelectChange(e as SelectChangeEvent<string[]>)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<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>
|
||||
</CustomTextField>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)
|
||||
case 2:
|
||||
return (
|
||||
<Fragment key={step}>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Twitter'
|
||||
value={twitter}
|
||||
onChange={e => setTwitter(e.target.value)}
|
||||
placeholder='https://twitter.com/johndoe'
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Facebook'
|
||||
value={facebook}
|
||||
onChange={e => setFacebook(e.target.value)}
|
||||
placeholder='https://facebook.com/johndoe'
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Google+'
|
||||
value={google}
|
||||
onChange={e => setGoogle(e.target.value)}
|
||||
placeholder='https://plus.google.com/johndoe'
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='LinkedIn'
|
||||
value={linkedIn}
|
||||
onChange={e => setLinkedIn(e.target.value)}
|
||||
placeholder='https://linkedin.com/johndoe'
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)
|
||||
default:
|
||||
return 'Unknown Step'
|
||||
}
|
||||
}
|
||||
|
||||
const renderContent = () => {
|
||||
if (activeStep === steps.length) {
|
||||
return (
|
||||
<>
|
||||
<Typography>All steps are completed!</Typography>
|
||||
<Box sx={{ mt: 4, display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<Button variant='contained' onClick={handleReset}>
|
||||
Reset
|
||||
</Button>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<form onSubmit={e => e.preventDefault()}>
|
||||
<Grid container spacing={5}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Typography variant='body2' sx={{ fontWeight: 600, color: 'text.primary' }}>
|
||||
{steps[activeStep].title}
|
||||
</Typography>
|
||||
<Typography variant='caption' component='p'>
|
||||
{steps[activeStep].subtitle}
|
||||
</Typography>
|
||||
</Grid>
|
||||
{getStepContent(activeStep)}
|
||||
<Grid size={{ xs: 12 }} sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Button
|
||||
variant='tonal'
|
||||
color='secondary'
|
||||
disabled={activeStep === 0}
|
||||
onClick={handleBack}
|
||||
startIcon={<DirectionalIcon ltrIconClass='tabler-arrow-left' rtlIconClass='tabler-arrow-right' />}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Button
|
||||
variant='contained'
|
||||
onClick={handleNext}
|
||||
endIcon={
|
||||
activeStep === steps.length - 1 ? (
|
||||
<i className='tabler-check' />
|
||||
) : (
|
||||
<DirectionalIcon ltrIconClass='tabler-arrow-right' rtlIconClass='tabler-arrow-left' />
|
||||
)
|
||||
}
|
||||
>
|
||||
{activeStep === steps.length - 1 ? 'Submit' : 'Next'}
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Card sx={{ display: 'flex', flexDirection: { xs: 'column', md: 'row' } }}>
|
||||
<StepperHeaderContainer>
|
||||
<StepperWrapper sx={{ height: '100%' }}>
|
||||
<Stepper
|
||||
activeStep={activeStep}
|
||||
orientation='vertical'
|
||||
connector={<></>}
|
||||
sx={{ height: '100%', minWidth: '15rem' }}
|
||||
>
|
||||
{steps.map((step, index) => {
|
||||
// const RenderAvatar = activeStep >= index ? CustomAvatar : Avatar
|
||||
|
||||
return (
|
||||
<Step key={index}>
|
||||
<StepLabel>
|
||||
<div className='step-label'>
|
||||
<CustomAvatar
|
||||
variant='rounded'
|
||||
skin={activeStep === index ? 'filled' : 'light'}
|
||||
{...(activeStep >= index && { color: 'primary' })}
|
||||
{...(activeStep === index && { className: 'shadow-primarySm' })}
|
||||
size={38}
|
||||
>
|
||||
<i className={classnames(step.icon)} />
|
||||
</CustomAvatar>
|
||||
<div>
|
||||
<Typography className='step-title'>{step.title}</Typography>
|
||||
<Typography className='step-subtitle'>{step.subtitle}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</StepLabel>
|
||||
</Step>
|
||||
)
|
||||
})}
|
||||
</Stepper>
|
||||
</StepperWrapper>
|
||||
</StepperHeaderContainer>
|
||||
<Divider sx={{ m: '0 !important' }} />
|
||||
<CardContent sx={{ width: '100%' }}>{renderContent()}</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default StepperCustomVertical
|
||||
@@ -0,0 +1,612 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import { styled } from '@mui/material/styles'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Card from '@mui/material/Card'
|
||||
import Step from '@mui/material/Step'
|
||||
import Button from '@mui/material/Button'
|
||||
import Divider from '@mui/material/Divider'
|
||||
import MuiStepper from '@mui/material/Stepper'
|
||||
import MenuItem from '@mui/material/MenuItem'
|
||||
import StepLabel from '@mui/material/StepLabel'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import FormHelperText from '@mui/material/FormHelperText'
|
||||
import InputAdornment from '@mui/material/InputAdornment'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import type { StepperProps } from '@mui/material/Stepper'
|
||||
|
||||
// Third-party Imports
|
||||
import { toast } from 'react-toastify'
|
||||
import { Controller, useForm } from 'react-hook-form'
|
||||
import { valibotResolver } from '@hookform/resolvers/valibot'
|
||||
import { email, object, minLength, string, array, forward, pipe, nonEmpty, check } from 'valibot'
|
||||
|
||||
// Component Imports
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
import StepperWrapper from '@core/styles/stepper'
|
||||
import StepperCustomDot from '@components/stepper-dot'
|
||||
import DirectionalIcon from '@components/DirectionalIcon'
|
||||
|
||||
// Vars
|
||||
const steps = [
|
||||
{
|
||||
title: 'Account Details',
|
||||
subtitle: 'Enter your account details'
|
||||
},
|
||||
{
|
||||
title: 'Personal Info',
|
||||
subtitle: 'Setup Information'
|
||||
},
|
||||
{
|
||||
title: 'Social Links',
|
||||
subtitle: 'Add Social Links'
|
||||
}
|
||||
]
|
||||
|
||||
// Styled Components
|
||||
const Stepper = styled(MuiStepper)<StepperProps>(({ theme }) => ({
|
||||
justifyContent: 'center',
|
||||
'& .MuiStep-root': {
|
||||
'&:first-of-type': {
|
||||
paddingInlineStart: 0
|
||||
},
|
||||
'&:last-of-type': {
|
||||
paddingInlineEnd: 0
|
||||
},
|
||||
[theme.breakpoints.down('md')]: {
|
||||
paddingInline: 0
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
const accountValidationSchema = object({
|
||||
username: pipe(string(), nonEmpty('This field is required'), minLength(1)),
|
||||
email: pipe(string(), nonEmpty('This field is required'), email('Please enter a valid email address')),
|
||||
password: pipe(
|
||||
string(),
|
||||
nonEmpty('This field is required'),
|
||||
minLength(8, 'Password must be at least 8 characters long')
|
||||
),
|
||||
confirmPassword: pipe(string(), nonEmpty('This field is required'), minLength(1))
|
||||
})
|
||||
|
||||
const accountSchema = pipe(
|
||||
accountValidationSchema,
|
||||
forward(
|
||||
check(input => input.password === input.confirmPassword, 'Passwords do not match.'),
|
||||
['confirmPassword']
|
||||
)
|
||||
)
|
||||
|
||||
const personalSchema = object({
|
||||
firstName: pipe(string(), nonEmpty('This field is required'), minLength(1)),
|
||||
lastName: pipe(string(), nonEmpty('This field is required'), minLength(1)),
|
||||
country: pipe(string(), nonEmpty('This field is required'), minLength(1)),
|
||||
language: pipe(array(string()), nonEmpty('This field is required'), minLength(1))
|
||||
})
|
||||
|
||||
const socialSchema = object({
|
||||
twitter: pipe(string(), nonEmpty('This field is required'), minLength(1)),
|
||||
facebook: pipe(string(), nonEmpty('This field is required'), minLength(1)),
|
||||
google: pipe(string(), nonEmpty('This field is required'), minLength(1)),
|
||||
linkedIn: pipe(string(), nonEmpty('This field is required'), minLength(1))
|
||||
})
|
||||
|
||||
const StepperLinearWithValidation = () => {
|
||||
// States
|
||||
const [activeStep, setActiveStep] = useState(0)
|
||||
const [isPasswordShown, setIsPasswordShown] = useState(false)
|
||||
const [isConfirmPasswordShown, setIsConfirmPasswordShown] = useState(false)
|
||||
|
||||
// Vars
|
||||
const Languages = ['English', 'French', 'Spanish', 'Portuguese', 'Italian', 'German', 'Arabic']
|
||||
|
||||
// Hooks
|
||||
const {
|
||||
reset: accountReset,
|
||||
control: accountControl,
|
||||
handleSubmit: handleAccountSubmit,
|
||||
formState: { errors: accountErrors }
|
||||
} = useForm({
|
||||
resolver: valibotResolver(accountSchema),
|
||||
defaultValues: {
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
confirmPassword: ''
|
||||
}
|
||||
})
|
||||
|
||||
const {
|
||||
reset: personalReset,
|
||||
control: personalControl,
|
||||
handleSubmit: handlePersonalSubmit,
|
||||
formState: { errors: personalErrors }
|
||||
} = useForm({
|
||||
resolver: valibotResolver(personalSchema),
|
||||
defaultValues: {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
country: '',
|
||||
language: []
|
||||
}
|
||||
})
|
||||
|
||||
const {
|
||||
reset: socialReset,
|
||||
control: socialControl,
|
||||
handleSubmit: handleSocialSubmit,
|
||||
formState: { errors: socialErrors }
|
||||
} = useForm({
|
||||
resolver: valibotResolver(socialSchema),
|
||||
defaultValues: {
|
||||
twitter: '',
|
||||
facebook: '',
|
||||
google: '',
|
||||
linkedIn: ''
|
||||
}
|
||||
})
|
||||
|
||||
const handleClickShowPassword = () => setIsPasswordShown(show => !show)
|
||||
|
||||
const handleClickShowConfirmPassword = () => setIsConfirmPasswordShown(show => !show)
|
||||
|
||||
const onSubmit = () => {
|
||||
setActiveStep(prevActiveStep => prevActiveStep + 1)
|
||||
|
||||
if (activeStep === steps.length - 1) {
|
||||
toast.success('Form Submitted')
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
setActiveStep(prevActiveStep => prevActiveStep - 1)
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
setActiveStep(0)
|
||||
accountReset({ email: '', username: '', password: '', confirmPassword: '' })
|
||||
personalReset({ firstName: '', lastName: '', country: '', language: [] })
|
||||
socialReset({ twitter: '', facebook: '', google: '', linkedIn: '' })
|
||||
setIsPasswordShown(false)
|
||||
setIsConfirmPasswordShown(false)
|
||||
}
|
||||
|
||||
const renderStepContent = (activeStep: number) => {
|
||||
switch (activeStep) {
|
||||
case 0:
|
||||
return (
|
||||
<form key={0} onSubmit={handleAccountSubmit(onSubmit)}>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
{steps[0].title}
|
||||
</Typography>
|
||||
<Typography variant='body2'>{steps[0].subtitle}</Typography>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='username'
|
||||
control={accountControl}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='Username'
|
||||
placeholder='johnDoe'
|
||||
{...(accountErrors.username && { error: true, helperText: accountErrors.username.message })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='email'
|
||||
control={accountControl}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
type='email'
|
||||
label='Email'
|
||||
placeholder='johndoe@gmail.com'
|
||||
{...(accountErrors.email && { error: true, helperText: accountErrors.email.message })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='password'
|
||||
control={accountControl}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='Password'
|
||||
placeholder='············'
|
||||
id='stepper-linear-validation-password'
|
||||
type={isPasswordShown ? 'text' : 'password'}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle password visibility'
|
||||
>
|
||||
<i className={isPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
{...(accountErrors.password && { error: true, helperText: accountErrors.password.message })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='confirmPassword'
|
||||
control={accountControl}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='Confirm Password'
|
||||
placeholder='············'
|
||||
id='stepper-linear-confirmPassword'
|
||||
type={isConfirmPasswordShown ? 'text' : 'password'}
|
||||
{...(accountErrors['confirmPassword'] && {
|
||||
error: true,
|
||||
helperText: accountErrors['confirmPassword'].message
|
||||
})}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position='end'>
|
||||
<IconButton
|
||||
edge='end'
|
||||
onClick={handleClickShowConfirmPassword}
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
aria-label='toggle password visibility'
|
||||
>
|
||||
<i className={isConfirmPasswordShown ? 'tabler-eye-off' : 'tabler-eye'} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }} className='flex justify-between'>
|
||||
<Button
|
||||
variant='tonal'
|
||||
disabled
|
||||
color='secondary'
|
||||
startIcon={<DirectionalIcon ltrIconClass='tabler-arrow-left' rtlIconClass='tabler-arrow-right' />}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Button
|
||||
variant='contained'
|
||||
type='submit'
|
||||
endIcon={<DirectionalIcon ltrIconClass='tabler-arrow-right' rtlIconClass='tabler-arrow-left' />}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
)
|
||||
case 1:
|
||||
return (
|
||||
<form key={1} onSubmit={handlePersonalSubmit(onSubmit)}>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
{steps[1].title}
|
||||
</Typography>
|
||||
<Typography variant='body2'>{steps[1].subtitle}</Typography>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='firstName'
|
||||
control={personalControl}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='First Name'
|
||||
placeholder='John'
|
||||
{...(personalErrors.firstName && {
|
||||
error: true,
|
||||
helperText: personalErrors.firstName.message
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='lastName'
|
||||
control={personalControl}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='Last Name'
|
||||
placeholder='Doe'
|
||||
{...(personalErrors.lastName && {
|
||||
error: true,
|
||||
helperText: personalErrors.lastName.message
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='country'
|
||||
control={personalControl}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
label='Country'
|
||||
{...field}
|
||||
error={Boolean(personalErrors.country)}
|
||||
>
|
||||
<MenuItem value='UK'>UK</MenuItem>
|
||||
<MenuItem value='USA'>USA</MenuItem>
|
||||
<MenuItem value='Australia'>Australia</MenuItem>
|
||||
<MenuItem value='Germany'>Germany</MenuItem>
|
||||
</CustomTextField>
|
||||
)}
|
||||
/>
|
||||
{personalErrors.country && <FormHelperText error>country is a required field</FormHelperText>}
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='language'
|
||||
control={personalControl}
|
||||
rules={{ required: true }}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<CustomTextField
|
||||
select
|
||||
fullWidth
|
||||
slotProps={{
|
||||
select: { multiple: true }
|
||||
}}
|
||||
label='Language'
|
||||
value={Array.isArray(value) ? value : []}
|
||||
onChange={onChange}
|
||||
error={Boolean(personalErrors.language)}
|
||||
>
|
||||
{Languages.map(language => (
|
||||
<MenuItem key={language} value={language}>
|
||||
{language}
|
||||
</MenuItem>
|
||||
))}
|
||||
</CustomTextField>
|
||||
)}
|
||||
/>
|
||||
{personalErrors.language && <FormHelperText error>language is a required field</FormHelperText>}
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }} className='flex justify-between'>
|
||||
<Button
|
||||
variant='tonal'
|
||||
onClick={handleBack}
|
||||
color='secondary'
|
||||
startIcon={<DirectionalIcon ltrIconClass='tabler-arrow-left' rtlIconClass='tabler-arrow-right' />}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Button
|
||||
variant='contained'
|
||||
type='submit'
|
||||
endIcon={<DirectionalIcon ltrIconClass='tabler-arrow-right' rtlIconClass='tabler-arrow-left' />}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
)
|
||||
case 2:
|
||||
return (
|
||||
<form key={2} onSubmit={handleSocialSubmit(onSubmit)}>
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<Typography className='font-medium' color='text.primary'>
|
||||
{steps[2].title}
|
||||
</Typography>
|
||||
<Typography variant='body2'>{steps[2].subtitle}</Typography>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='twitter'
|
||||
control={socialControl}
|
||||
rules={{ required: true }}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<CustomTextField
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
fullWidth
|
||||
label='Twitter'
|
||||
placeholder='https://twitter.com/johndoe'
|
||||
{...(socialErrors.twitter && { error: true, helperText: socialErrors.twitter.message })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='facebook'
|
||||
control={socialControl}
|
||||
rules={{ required: true }}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<CustomTextField
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
fullWidth
|
||||
label='Facebook'
|
||||
placeholder='https://facebook.com/johndoe'
|
||||
{...(socialErrors.facebook && { error: true, helperText: socialErrors.facebook.message })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='google'
|
||||
control={socialControl}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='Google'
|
||||
placeholder='https://google.com/johndoe'
|
||||
{...(socialErrors.google && { error: true, helperText: socialErrors.google.message })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6 }}>
|
||||
<Controller
|
||||
name='linkedIn'
|
||||
control={socialControl}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => (
|
||||
<CustomTextField
|
||||
{...field}
|
||||
fullWidth
|
||||
label='LinkedIn'
|
||||
placeholder='https://linkedin.com/johndoe'
|
||||
{...(socialErrors.linkedIn && { error: true, helperText: socialErrors.linkedIn.message })}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }} className='flex justify-between'>
|
||||
<Button
|
||||
variant='tonal'
|
||||
onClick={handleBack}
|
||||
color='secondary'
|
||||
startIcon={<DirectionalIcon ltrIconClass='tabler-arrow-left' rtlIconClass='tabler-arrow-right' />}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Button variant='contained' type='submit' endIcon={<i className='tabler-check' />}>
|
||||
Submit
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
)
|
||||
default:
|
||||
return <Typography>Unknown stepIndex</Typography>
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<StepperWrapper>
|
||||
<Stepper activeStep={activeStep}>
|
||||
{steps.map((label, index) => {
|
||||
const labelProps: {
|
||||
error?: boolean
|
||||
} = {}
|
||||
|
||||
if (index === activeStep) {
|
||||
labelProps.error = false
|
||||
|
||||
if (
|
||||
(accountErrors.email ||
|
||||
accountErrors.username ||
|
||||
accountErrors.password ||
|
||||
accountErrors['confirmPassword']) &&
|
||||
activeStep === 0
|
||||
) {
|
||||
labelProps.error = true
|
||||
} else if (
|
||||
(personalErrors.firstName ||
|
||||
personalErrors.lastName ||
|
||||
personalErrors.country ||
|
||||
personalErrors.language) &&
|
||||
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} className='max-md:mbe-5'>
|
||||
<StepLabel
|
||||
{...labelProps}
|
||||
slots={{
|
||||
stepIcon: StepperCustomDot
|
||||
}}
|
||||
>
|
||||
<div className='step-label'>
|
||||
<Typography className='step-number'>{`0${index + 1}`}</Typography>
|
||||
<div>
|
||||
<Typography className='step-title' color='text.primary'>
|
||||
{label.title}
|
||||
</Typography>
|
||||
<Typography className='step-subtitle'>{label.subtitle}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</StepLabel>
|
||||
</Step>
|
||||
)
|
||||
})}
|
||||
</Stepper>
|
||||
</StepperWrapper>
|
||||
</CardContent>
|
||||
<Divider />
|
||||
<CardContent>
|
||||
{activeStep === steps.length ? (
|
||||
<>
|
||||
<Typography className='mlb-2 mli-1'>All steps are completed!</Typography>
|
||||
<div className='flex justify-end mt-4'>
|
||||
<Button variant='contained' onClick={handleReset}>
|
||||
Reset
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
renderStepContent(activeStep)
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default StepperLinearWithValidation
|
||||
@@ -0,0 +1,116 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Step from '@mui/material/Step'
|
||||
import Card from '@mui/material/Card'
|
||||
import Button from '@mui/material/Button'
|
||||
import Stepper from '@mui/material/Stepper'
|
||||
import StepLabel from '@mui/material/StepLabel'
|
||||
import CardHeader from '@mui/material/CardHeader'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import StepContent from '@mui/material/StepContent'
|
||||
|
||||
// Third-party Imports
|
||||
import { toast } from 'react-toastify'
|
||||
import classNames from 'classnames'
|
||||
|
||||
// Component Imports
|
||||
import StepperWrapper from '@core/styles/stepper'
|
||||
import StepperCustomDot from '@components/stepper-dot'
|
||||
|
||||
// Vars
|
||||
const steps = [
|
||||
{
|
||||
title: 'Account Details',
|
||||
subtitle: 'Enter your Account Details',
|
||||
description:
|
||||
'Chocolate cookie lollipop toffee candy canes marzipan liquorice chocolate. Cake gummi bears dessert lollipop apple pie candy. Candy pie sesame snaps lollipop biscuit chocolate cake fruitcake apple pie. Toffee carrot cake biscuit oat cake jujubes fruitcake biscuit gummi bears. Cake carrot cake jujubes sugar plum pastry gummi bears gingerbread icing. Lemon drops pie cake. Halvah marzipan bonbon gingerbread cupcake pastry gummi bears cake jujubes.'
|
||||
},
|
||||
{
|
||||
title: 'Personal Info',
|
||||
subtitle: 'Setup Information',
|
||||
description:
|
||||
'Lemon drops ice cream danish macaroon bear claw cookie. Liquorice ice cream chocolate bar pastry chocolate bar candy. Caramels candy canes marshmallow soufflé biscuit tart fruitcake tiramisu. Gummi bears icing gingerbread pastry bonbon gummies candy canes pastry. Candy canes chocolate chupa chups cake cheesecake apple pie halvah dessert. Chupa chups wafer tootsie roll fruitcake lemon drops cookie donut topping powder.'
|
||||
},
|
||||
{
|
||||
title: 'Social Links',
|
||||
subtitle: 'Add Social Links',
|
||||
description:
|
||||
'Jelly lollipop halvah bear claw jujubes macaroon candy canes. Soufflé halvah lollipop liquorice macaroon powder. Cookie topping pastry oat cake caramels bonbon. Sesame snaps sweet cookie macaroon soufflé pudding. Chocolate donut macaroon muffin donut biscuit marzipan halvah. Bear claw biscuit chocolate cake chupa chups oat cake bear claw cupcake tiramisu apple pie. Carrot cake bear claw marshmallow sweet pudding toffee.'
|
||||
}
|
||||
]
|
||||
|
||||
const StepperVerticalWithNumbers = () => {
|
||||
// States
|
||||
const [activeStep, setActiveStep] = useState(0)
|
||||
|
||||
const handleNext = () => {
|
||||
setActiveStep(prevActiveStep => prevActiveStep + 1)
|
||||
|
||||
if (activeStep === steps.length - 1) {
|
||||
toast.success('Completed All Steps!!')
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
setActiveStep(prevActiveStep => prevActiveStep - 1)
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
setActiveStep(0)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Vertical Stepper with Numbers' />
|
||||
<CardContent>
|
||||
<StepperWrapper>
|
||||
<Stepper activeStep={activeStep} orientation='vertical'>
|
||||
{steps.map((step, index) => (
|
||||
<Step key={index} className={classNames({ active: activeStep === index })}>
|
||||
<StepLabel
|
||||
slots={{
|
||||
stepIcon: 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>
|
||||
<StepContent>
|
||||
<Typography>{step.description}</Typography>
|
||||
<div className='flex gap-4 mt-4'>
|
||||
<Button variant='contained' onClick={handleNext} size='small'>
|
||||
{index === steps.length - 1 ? 'Finish' : 'Next'}
|
||||
</Button>
|
||||
<Button size='small' color='secondary' variant='tonal' onClick={handleBack} disabled={index === 0}>
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
</StepContent>
|
||||
</Step>
|
||||
))}
|
||||
</Stepper>
|
||||
</StepperWrapper>
|
||||
{activeStep === steps.length && (
|
||||
<div className='mt-2'>
|
||||
<Typography>All steps are completed!</Typography>
|
||||
<Button variant='contained' onClick={handleReset} size='small' className='mt-2'>
|
||||
Reset
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default StepperVerticalWithNumbers
|
||||
@@ -0,0 +1,115 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Step from '@mui/material/Step'
|
||||
import Card from '@mui/material/Card'
|
||||
import Button from '@mui/material/Button'
|
||||
import Stepper from '@mui/material/Stepper'
|
||||
import StepLabel from '@mui/material/StepLabel'
|
||||
import CardHeader from '@mui/material/CardHeader'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import StepContent from '@mui/material/StepContent'
|
||||
|
||||
// Third-party Imports
|
||||
import { toast } from 'react-toastify'
|
||||
import classNames from 'classnames'
|
||||
|
||||
// Component Imports
|
||||
import StepperWrapper from '@core/styles/stepper'
|
||||
import StepperCustomDot from '@components/stepper-dot'
|
||||
|
||||
// Vars
|
||||
const steps = [
|
||||
{
|
||||
title: 'Account Details',
|
||||
subtitle: 'Enter your Account Details',
|
||||
description:
|
||||
'Chocolate cookie lollipop toffee candy canes marzipan liquorice chocolate. Cake gummi bears dessert lollipop apple pie candy. Candy pie sesame snaps lollipop biscuit chocolate cake fruitcake apple pie. Toffee carrot cake biscuit oat cake jujubes fruitcake biscuit gummi bears. Cake carrot cake jujubes sugar plum pastry gummi bears gingerbread icing. Lemon drops pie cake. Halvah marzipan bonbon gingerbread cupcake pastry gummi bears cake jujubes.'
|
||||
},
|
||||
{
|
||||
title: 'Personal Info',
|
||||
subtitle: 'Setup Information',
|
||||
description:
|
||||
'Lemon drops ice cream danish macaroon bear claw cookie. Liquorice ice cream chocolate bar pastry chocolate bar candy. Caramels candy canes marshmallow soufflé biscuit tart fruitcake tiramisu. Gummi bears icing gingerbread pastry bonbon gummies candy canes pastry. Candy canes chocolate chupa chups cake cheesecake apple pie halvah dessert. Chupa chups wafer tootsie roll fruitcake lemon drops cookie donut topping powder.'
|
||||
},
|
||||
{
|
||||
title: 'Social Links',
|
||||
subtitle: 'Add Social Links',
|
||||
description:
|
||||
'Jelly lollipop halvah bear claw jujubes macaroon candy canes. Soufflé halvah lollipop liquorice macaroon powder. Cookie topping pastry oat cake caramels bonbon. Sesame snaps sweet cookie macaroon soufflé pudding. Chocolate donut macaroon muffin donut biscuit marzipan halvah. Bear claw biscuit chocolate cake chupa chups oat cake bear claw cupcake tiramisu apple pie. Carrot cake bear claw marshmallow sweet pudding toffee.'
|
||||
}
|
||||
]
|
||||
|
||||
const StepperVerticalWithoutNumbers = () => {
|
||||
// States
|
||||
const [activeStep, setActiveStep] = useState(0)
|
||||
|
||||
const handleNext = () => {
|
||||
setActiveStep(prevActiveStep => prevActiveStep + 1)
|
||||
|
||||
if (activeStep === steps.length - 1) {
|
||||
toast.success('Completed All Steps!!')
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
setActiveStep(prevActiveStep => prevActiveStep - 1)
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
setActiveStep(0)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Vertical Stepper without Numbers' />
|
||||
<CardContent>
|
||||
<StepperWrapper>
|
||||
<Stepper activeStep={activeStep} orientation='vertical'>
|
||||
{steps.map((step, index) => (
|
||||
<Step key={index} className={classNames({ active: activeStep === index })}>
|
||||
<StepLabel
|
||||
slots={{
|
||||
stepIcon: StepperCustomDot
|
||||
}}
|
||||
>
|
||||
<div className='step-label'>
|
||||
<div>
|
||||
<Typography className='step-title'>{step.title}</Typography>
|
||||
<Typography className='step-subtitle'>{step.subtitle}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</StepLabel>
|
||||
<StepContent>
|
||||
<Typography>{step.description}</Typography>
|
||||
<div className='flex gap-4 mt-4'>
|
||||
<Button variant='contained' onClick={handleNext} size='small'>
|
||||
{index === steps.length - 1 ? 'Finish' : 'Next'}
|
||||
</Button>
|
||||
<Button size='small' color='secondary' variant='tonal' onClick={handleBack} disabled={index === 0}>
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
</StepContent>
|
||||
</Step>
|
||||
))}
|
||||
</Stepper>
|
||||
</StepperWrapper>
|
||||
{activeStep === steps.length && (
|
||||
<div className='mt-2'>
|
||||
<Typography>All steps are completed!</Typography>
|
||||
<Button variant='contained' onClick={handleReset} className='mt-2' size='small'>
|
||||
Reset
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default StepperVerticalWithoutNumbers
|
||||
Reference in New Issue
Block a user