Disable button on Form Submit in React - reactjs

For some reason, my Sign In button does not become disabled when the form is submitted. Is there something obviously incorrect?
It is supposed to be disabled when processing a user's sign in API request.
This is also not a result of a fast API, because even if I set the network speed to very slow, it does not become disabled at any point.
import React, { useRef, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import {
Alert, Box, Card, TextField, Typography,
} from '#mui/material';
import LoadingButton from '#mui/lab/LoadingButton';
import { useAuth } from '../context/AuthContext';
import '../../pages/bg.css';
export default function SignInAuth() {
const emailRef = useRef();
const passwordRef = useRef();
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
const auth = useAuth();
async function handleSubmit(e) {
e.preventDefault();
if (!emailRef.current.value) {
return setError('Please enter an Email Address');
}
if (!passwordRef.current.value) {
return setError('Please enter a password');
}
setLoading(true);
setError('');
fetch(
`${process.env.REACT_APP_API_URL}auth/login`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
Email: emailRef.current.value,
Password: passwordRef.current.value,
}),
},
).then((response) => {
if (response.ok) {
auth.signin(emailRef.current.value);
return navigate('/Dashboard');
}
// Response must be handled in a promise.
return response.json().then((errorText) => { throw new Error(errorText.message); });
}).then(setLoading(false)).catch((err) => {
setError(err.message); // This comes from the returned error promise.
// System throws error, and then error is set for the text.
});
return null;
}
const onSubmit = (e) => {
handleSubmit(e);
};
const textFieldStyle = {
width: '300px',
};
return (
<Box sx={{
zIndex: 1,
minWidth: '200px',
width: '450px',
}}
>
<Card sx={{ p: '20px', borderRadius: '15px' }}>
<Typography variant="h4" sx={{ fontWeight: 600, textAlign: 'center', mb: '8px' }}>Sign In</Typography>
<form onSubmit={(e) => onSubmit(e)}>
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
{error && <Alert severity="error" sx={{ ...textFieldStyle, my: '10px' }}>{error}</Alert>}
<TextField autoFocus margin="normal" inputRef={emailRef} type="email" id="email" label="Email" sx={textFieldStyle} />
<TextField margin="normal" inputRef={passwordRef} type="password" id="password" label="Password" sx={textFieldStyle} />
<LoadingButton
sx={{ mt: '16px', mb: '4px', width: '150px' }}
loading={loading}
variant="contained"
type="submit"
>
Sign In
</LoadingButton>
</Box>
</form>
<Typography variant="subtitle1" align="center" sx={{ mt: '10px' }}>
<Link to="/forgot-password">Forgot Password?</Link>
</Typography>
</Card>
<Box>
<Typography variant="subtitle1" align="center" sx={{ mt: '10px' }}>
Need an account?
{' '}
<Link to="/SignUp">Sign Up</Link>
</Typography>
</Box>
</Box>
);
}

According to the documentation, you must pass in a disabled prop to the LoadingButton component. Set it to be the same as your loading state.
<LoadingButton
sx={{ mt: '16px', mb: '4px', width: '150px' }}
loading={loading}
disabled={loading}
variant="contained"
type="submit"
>
Sign In
</LoadingButton>

Related

React.js + MUI: Modal closes when clicking on Select component

for MUI learning purposes I'm creating a simple CRUD app with a modal. That modal contains a simple form with a few TextField and one Select components. THe issue is, that when clicking on the Select component, the modal closes.
Modal:
<ClickAwayListener
onClickAway={handleClickAway}
>
<Box sx={{ marginTop: '80px' }}>
<Button
sx={{
borderRadius: '8px',
backgroundColor: '#fff',
color: '#091fbb',
border: '1px solid #091fbb'
}}
onClick={handleOpen}
>
Add new
</Button>
<Modal
hideBackdrop
open={open}
onClose={handleClose}
sx={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
backgroundColor: '#fff',
border: '1px solid #b9c2ff',
borderRadius: '8px',
height: 'fit-content',
width: 400,
boxShadow: 2,
}}
>
<form
onSubmit={handleSubmit}
style={{
display: 'flex',
flexDirection: 'column',
paddingTop: '12px',
paddingLeft: '18px',
paddingRight: '18px',
paddingBottom: '30px',
}}
>
<Typography variant='h6' sx={{ my: 2, textAlign: 'center' }}>ADD NEW PARTICIPANT</Typography>
<FormControl sx={{ my: 1 }}>
<Typography variant='body2'>Fullname</Typography>
<TextField
variant='standard'
value={fullname}
onChange={(e) => setFullname(e.target.value)}
/>
</FormControl>
<FormControl sx={{ my: 1 }}>
<Typography variant='body2'>Gender</Typography>
<Select
variant='standard'
value={gender}
MenuProps={{
onClick: e => {
e.preventDefault();
}
}}
onChange={(e) => setGender(e.target.value)}
>
<MenuItem value="None"><em>None</em></MenuItem>
<MenuItem value='Male'>Male</MenuItem>
<MenuItem value='Female'>Female</MenuItem>
<MenuItem value='Other'>Other</MenuItem>
</Select>
</FormControl>
<FormControl sx={{ my: 1 }}>
<Typography variant='body2'>Email</Typography>
<TextField
variant='standard'
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</FormControl>
<FormControl sx={{ my: 1 }}>
<Typography variant='body2'>Phone nr</Typography>
<TextField
variant='standard'
value={phone}
onChange={(e) => setPhone(e.target.value)}
/>
</FormControl>
<FormControl sx={{ my: 1 }}>
<Typography variant='body2'>Description</Typography>
<TextField
variant='standard'
value={description}
onChange={(e) => setDescription(e.target.value)}
multiline
rows={3}
/>
</FormControl>
{ !isLoading && <Button
variant='contained'
type='submit'
sx={{
backgroundColor: '#091fbb'
}}>
Add participant
</Button>}
{ isLoading && <Button
variant='contained'
type='submit'
disabled
sx={{
backgroundColor: '#091fbb'
}}>
Adding participant...
</Button>}
</form>
</Modal>
</Box>
</ClickAwayListener>
Handler functions and states for Modal:
const [open, setOpen] = useState(false);
const [fullname, setFullname] = useState('');
const [gender, setGender] = useState('None');
const [email, setEmail] = useState('');
const [phone, setPhone] = useState('');
const [description, setDescription] = useState('');
const [isLoading, setIsLoading] = useState(false);
const handleOpen = () => {
setOpen(!open);
};
const handleClose = () => {
setFullname('');
setGender('None');
setEmail('');
setPhone('');
setDescription('');
setOpen(false);
};
const handleClickAway = (e) => {
if (!e.target.classList.contains('MuiMenuItem-root')) {
setFullname('');
setGender('None');
setEmail('');
setPhone('');
setDescription('');
setOpen(false);
}
};
const handleSubmit = (e) => {
e.preventDefault();
const newParticipant = { fullname, gender, email, phone, description };
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newParticipant)
};
setIsLoading(true);
fetch('http://localhost:8000/participants', requestOptions)
.then(() => {
setFullname('');
setGender('None');
setEmail('');
setPhone('');
setDescription('');
setIsLoading(false);
setOpen(!open);
})
};
Could anyone advise on how to solve this? Adding MenuProps to prevent default behavior on the Select component and the if statement in handleClickAway function didnt help in my case, even though that helped other who were facing the same issue.
Assuming that the goal is to have Select work in Modal without closing it, perhaps the default behavior of Modal could be enough and use of ClickAwayListener may be not be necessary.
Instead of styling Modal directly with the sx prop, try wrap the modal content in a Box and style this container. This preserves the default behavior of Modal, so that clicking on Select would not trigger the closing of it.
Since Modal internally detect click on the backdrop to close itself, consider to style the backdrop with a transparent background instead of disabling it, so that the use of ClickAwayListener could also be omitted.
Demo of simplified example on: stackblitz (excluded all data handling)
<Modal
open={open}
onClose={handleClose}
// 👇 Style the backdrop to be transparent
slotProps={{ backdrop: { sx: { background: "transparent" } } }}
>
<Box
// 👇 Style the container Box for modal content
sx={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
backgroundColor: "#fff",
border: "1px solid #b9c2ff",
borderRadius: "8px",
height: "fit-content",
width: 400,
boxShadow: 2,
}}
>
{/* Modal content here */}
</Box>
</Modal>
This happens because the menu is by default mounted in the DOM outside of the modal HTML hierarchy; the Select component uses a Menu component which in turn uses a Popper component. Looking at the API documentation for Popper:
The children will be under the DOM hierarchy of the parent component.
disablePortal:bool = false
A simple solution is to override this default in MenuProps, which will cause the component to be rendered as a child to the Modal and will no longer trigger the ClickAwayListener callback. I'm not aware of any downsides to this approach.
<Select
variant='standard'
value={gender}
MenuProps={{
disablePortal: true, // <--- HERE
onClick: e => {
e.preventDefault();
}
}}
onChange={(e) => setGender(e.target.value)}
> . . . </Select>

How can we validate our form using formik

I am unable to validate the following form because of checkbox handling. I have to implement this type of thing in many of my form issue is same i am unable to handle the on change on some of that form to handle the checkbox.
import React, { useEffect } from "react";
import {
Grid,
Typography,
Button,
Box,
TextField,
FormControlLabel,
} from "#mui/material";
import { Formik, Form, Field, ErrorMessage } from "formik";
import axios from "axios";
import * as Yup from "yup";
import API from "../../../../../E2E/Axios.Utils";
const AddReport = ({
handleDialog,
update,
setUpdate,
setAlert,
setAlertContent,
setAlertType,
}) => {
const [tabData, setTabData] = React.useState([]);
const token = localStorage.getItem("token").toString();
const onSubmit = (values, props) => {
console.log("values", values);
const tabs = [];
values.tab_id.map((ele) => {
if (typeof ele === "string") {
tabs.push(Number(ele));
}
console.log("tabs", tabs);
return tabs;
});
const data = {
report_type_name: values.report_type_name,
description: values.description,
tab_id: tabs,
remember: false,
};
API.post("/app/secure/report_type/", data, {
headers: { Authorization: `Bearer ${token}` },
})
.then((res) => {
handleDialog();
setUpdate(update + 1);
setAlertContent(res.data.msg);
setAlertType("success");
setAlert(true);
})
.catch((err) => {
console.log(err);
if (err?.response?.status == 401) {
setAlertContent("Token is invalid or expired.");
setAlertType("error");
setAlert(true);
} else {
setAlertContent(`Error : Something went wrong.`);
setAlertType("error");
setAlert(true);
}
});
props.resetForm();
};
This API is used to get the tab details
const getTabData = () => {
API.get("/app/secure/my_tab/", {
headers: { Authorization: `Bearer ${token}` },
})
.then((res) => {
console.log("GetData", res);
setTabData(res.data.tab_list);
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
};
useEffect(() => {
getTabData();
}, []);
const initialValues = {
report_type_name: "",
description: "",
tab_id: [],
remember: false,
};
const AlphaNumeric = /^[a-zA-Z0-9](([_ -])?[a-zA-Z0-9]*)+$/;
const validationSchema = Yup.object().shape({
report_type_name: Yup.string()
.matches(AlphaNumeric, "Please enter valid report type name.")
.max(40, "Tab Name must be at most 40 characters.")
.required("This field is required."),
description: Yup.string()
.matches(AlphaNumeric, "Please enter valid description.")
.max(160, "Description must be at most 160 characters.")
.required("This field is required."),
});
return (
<Box>
<Typography
id="modal-modal-title"
variant="h6"
component="h2"
sx={{ marginBottom: "4%" }}
>
Add Report
</Typography>
<Box>
<Formik
initialValues={initialValues}
onSubmit={onSubmit}
validationSchema={validationSchema}
>
{(props) => (
<Form noValidate>
<Field
as={TextField}
name="report_type_name"
label="Name"
placeholder="Name"
fullWidth
error={
props.errors.report_type_name &&
props.touched.report_type_name
}
size="small"
helperText={<ErrorMessage name="report_type_name" />}
required
sx={{ marginBottom: "4%" }}
/>
<Field
as={TextField}
name="description"
label="Description"
placeholder="Description"
fullWidth
error={props.errors.description && props.touched.description}
size="small"
helperText={<ErrorMessage name="description" />}
required
sx={{ marginBottom: "4%" }}
/>
<Typography variant="subtitle1" sx={{ textAlign: "left" }}>
<span style={{ fontWeight: "bold" }}>Tabs</span>
</Typography>
<Grid container>
{tabData.map((item) => {
return (
<Grid
item
xl={4}
lg={4}
md={4}
sm={12}
xs={12}
key={item.tab_id}
style={{
display: "flex",
width: "150px",
}}
>
<FormControlLabel
control={
<Field
name="tab_id"
label={item.tab_name}
value={item.tab_id.toString()}
style={{ margin: "8px" }}
type="checkbox"
/>
}
/>
<div style={{ margin: "8px" }}>{item.tab_name}</div>
</Grid>
);
})}
</Grid>
<Grid
container
sx={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
gap: "1rem",
mt: 2,
}}
>
<Grid item>
<Button onClick={handleDialog}>Disagree</Button>
</Grid>
<Grid item>
<Button
type="submit"
variant="contained"
sx={{
padding: "10px",
borderRadius: "20px",
width: "101px",
height: "36px",
}}
>
Save
</Button>
</Grid>
</Grid>
</Form>
)}
</Formik>
</Box>
</Box>
);
};
export default AddReport;
I have put all the code in this. facing same issue.
I dont know from where the error is coming.

I want to navigate to home page upon successfull authentication

I am working on a next.js theme. I want to navigate to the "dashboards/analytics" page upon successfull authentication. we are using JWT based authentication. Authentication is done. but i could not navigate to the dashboards page.I really could not understand what is wrong in this code. Also i am getting a "Password is required error" even after passing a value. but it is not effecting the login function(i was able to login though, with the same password value). i tried commenting out the "yup resolver" code. but it didnt show any error when i clicked login button without a password(but login was not successfull). Kindly someone help me to fix these issues.
AuthContext.js file is as shown below
// ** React Imports
import { createContext, useEffect, useState } from 'react'
// ** Next Import
import {useRouter} from 'next/router'
// ** Axios
import axios from 'axios'
// ** Config
import authConfig from 'src/configs/auth'
// ** Defaults
const defaultProvider={
user: null,
loading: true,
setUser: () => null,
setLoading: () => Boolean,
isInitialized: false,
login: (token) => {},
logout: () => Promise.resolve(),
setIsInitialized: () => Boolean,
register: () => Promise.resolve(),
token:'',
};
const AuthContext = createContext(defaultProvider)
const AuthProvider = ({ children }) => {
// ** States
const [user, setUser] = useState(defaultProvider.user)
const [loading, setLoading] = useState(defaultProvider.loading)
const [isInitialized, setIsInitialized] = useState(defaultProvider.isInitialized)
const [token,setToken]=useState(defaultProvider.token)
// ** Hooks
const router = useRouter()
useEffect(() => {
const initAuth = async () => {
setIsInitialized(true)
const storedToken = window.localStorage.getItem(authConfig.storageTokenKeyName)
if (storedToken) {
setLoading(true)
await axios
.get(authConfig.meEndpoint, {
headers: {
Authorization: storedToken
}
})
.then(async response => {
setLoading(false)
setUser({ ...response.data.userData })
})
} else {
setLoading(false)
}
}
initAuth()
}, [])
const handleLogin=(token)=>{
setToken(token);
router.push('/dashboards/analytics');
}
const handleLogout = () => {
setUser(null)
setIsInitialized(false)
window.localStorage.removeItem('userData')
window.localStorage.removeItem(authConfig.storageTokenKeyName)
router.push('/login')
}
const handleRegister = (params, errorCallback) => {
axios
.post(authConfig.registerEndpoint, params)
.then(res => {
if (res.data.error) {
if (errorCallback) errorCallback(res.data.error)
} else {
handleLogin({ email: params.email, password: params.password })
}
})
.catch(err => (errorCallback ? errorCallback(err) : null))
}
const values = {
user,
loading,
setUser,
setLoading,
isInitialized,
setIsInitialized,
login: handleLogin,
logout: handleLogout,
register: handleRegister,
token
}
return <AuthContext.Provider value={values}>{children}</AuthContext.Provider>
}
export { AuthContext, AuthProvider }
Login/index.js file is as shown below,
// ** React Imports
import { useState, useContext } from 'react'
import {useRouter} from 'next/router'
// ** Next Imports
import Link from 'next/link'
import axios from 'axios'
// ** MUI Components
import Button from '#mui/material/Button'
import Divider from '#mui/material/Divider'
import Checkbox from '#mui/material/Checkbox'
import TextField from '#mui/material/TextField'
import InputLabel from '#mui/material/InputLabel'
import IconButton from '#mui/material/IconButton'
import Box from '#mui/material/Box'
import FormControl from '#mui/material/FormControl'
import useMediaQuery from '#mui/material/useMediaQuery'
import OutlinedInput from '#mui/material/OutlinedInput'
import { styled, useTheme } from '#mui/material/styles'
import FormHelperText from '#mui/material/FormHelperText'
import InputAdornment from '#mui/material/InputAdornment'
import Typography from '#mui/material/Typography'
import MuiFormControlLabel from '#mui/material/FormControlLabel'
// ** Icons Imports
import Google from 'mdi-material-ui/Google'
import Github from 'mdi-material-ui/Github'
import Twitter from 'mdi-material-ui/Twitter'
import Facebook from 'mdi-material-ui/Facebook'
import EyeOutline from 'mdi-material-ui/EyeOutline'
import EyeOffOutline from 'mdi-material-ui/EyeOffOutline'
// ** Third Party Imports
import * as yup from 'yup'
import { useForm, Controller } from 'react-hook-form'
import { yupResolver } from '#hookform/resolvers/yup'
// ** Hooks
import { useAuth } from 'src/hooks/useAuth'
import useBgColor from 'src/#core/hooks/useBgColor'
import { useSettings } from 'src/#core/hooks/useSettings'
// ** Configs
import themeConfig from 'src/configs/themeConfig'
// ** Layout Import
import BlankLayout from 'src/#core/layouts/BlankLayout'
// ** Demo Imports
import FooterIllustrationsV2 from 'src/views/pages/auth/FooterIllustrationsV2'
import { AuthContext } from 'src/context/AuthContext'
// ** Styled Components
const LoginIllustrationWrapper = styled(Box)(({ theme }) => ({
padding: theme.spacing(20),
paddingRight: '0 !important',
[theme.breakpoints.down('lg')]: {
padding: theme.spacing(10)
}
}))
const LoginIllustration = styled('img')(({ theme }) => ({
maxWidth: '48rem',
[theme.breakpoints.down('lg')]: {
maxWidth: '35rem'
}
}))
const RightWrapper = styled(Box)(({ theme }) => ({
width: '100%',
[theme.breakpoints.up('md')]: {
maxWidth: 450
}
}))
const BoxWrapper = styled(Box)(({ theme }) => ({
[theme.breakpoints.down('xl')]: {
width: '100%'
},
[theme.breakpoints.down('md')]: {
maxWidth: 400
}
}))
const TypographyStyled = styled(Typography)(({ theme }) => ({
fontWeight: 600,
marginBottom: theme.spacing(1.5),
[theme.breakpoints.down('md')]: { marginTop: theme.spacing(8) }
}))
const LinkStyled = styled('a')(({ theme }) => ({
fontSize: '0.875rem',
textDecoration: 'none',
color: theme.palette.primary.main
}))
const FormControlLabel = styled(MuiFormControlLabel)(({ theme }) => ({
'& .MuiFormControlLabel-label': {
fontSize: '0.875rem',
color: theme.palette.text.secondary
}
}))
const schema = yup.object().shape({
username: yup.string().required(),
password: yup.string().min(5 ).required()
})
const LoginPage = () => {
const [showPassword, setShowPassword] = useState(false)
const [username,setUsername]=useState('');
const [password,setPassword]=useState('');
//const [errMsg,setErrMsg]=useState('');
// ** Hooks
const auth = useContext(AuthContext);
const theme = useTheme()
const router=useRouter();
const bgClasses = useBgColor()
const {
settings: { skin }
} = useSettings()
const {
control,
setError,
//handleSubmit,
formState: { errors }
} = useForm({
//defaultValues,
mode: 'onBlur',
resolver: yupResolver(schema)
})
// ** Vars
const hidden = useMediaQuery(theme.breakpoints.down('md'))
const handleSubmit=(e)=>{
e.preventDefault();
fetch(
'url',
{
method:'POST',
body:JSON.stringify({
username:username,
password:password,
}),
headers: {
'Content-Type':'application/json',
'X-Correlation-ID':'123456',
'Source-ID':'abc123',
}
}).then(res =>{
if(res.ok){
return res.json();
}
else{
return res.json().then(data=> {
let errorMessage="Authentication Failed";
throw new Error(errorMessage);
});
}
}).then(data=>{
auth.login(data.response.accessToken);
}).catch(err=>{
alert(err.message);
});
}
return (
<Box className='content-right'>
{!hidden ? (
<Box sx={{ flex: 1, display: 'flex', position: 'relative', alignItems: 'center', justifyContent: 'center' }}>
<LoginIllustrationWrapper>
<LoginIllustration
alt='login-illustration'
src={`/images/pages/auth-v2-login-illustration-${theme.palette.mode}.png`}
/>
</LoginIllustrationWrapper>
<FooterIllustrationsV2 />
</Box>
) : null}
<RightWrapper sx={skin === 'bordered' && !hidden ? { borderLeft: `1px solid ${theme.palette.divider}` } : {}}>
<Box
sx={{
p: 12,
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'background.paper'
}}
>
<BoxWrapper>
<Box
sx={{
top: 30,
left: 40,
display: 'flex',
position: 'absolute',
alignItems: 'center',
justifyContent: 'center'
}}
>
<Typography
variant='h6'
sx={{
ml: 3,
lineHeight: 1,
fontWeight: 600,
textTransform: 'uppercase',
fontSize: '1.5rem !important'
}}
>
{themeConfig.templateName}
</Typography>
</Box>
<Box sx={{ mb: 6 }}>
<TypographyStyled variant='h5'>Welcome to {themeConfig.templateName}! 👋🏻</TypographyStyled>
<Typography variant='body2'>Please sign-in to your account and start the adventure</Typography>
</Box>
<form noValidate autoComplete='off' onSubmit={handleSubmit}>
<FormControl fullWidth sx={{ mb: 4 }}>
<Controller
name='Username'
control={control}
rules={{ required: true }}
render={({ field: { value, onChange, onBlur } }) => (
<TextField
autoFocus
label='Username'
value={value}
onBlur={onBlur}
//onChange={onChange}
onChange={(e)=> setUsername(e.target.value)}
error={Boolean(errors.user)}
// placeholder='admin#materio.com'
/>
)}
/>
{errors.user && <FormHelperText sx={{ color: 'error.main' }}>{errors.user.message}</FormHelperText>}
</FormControl>
<FormControl fullWidth>
<InputLabel htmlFor='auth-login-v2-password' error={Boolean(errors.password)}>
Password
</InputLabel>
<Controller
name='password'
control={control}
rules={{ required: true }}
render={({ field: { value,onChange, onBlur } }) => (
<OutlinedInput
value={value}
onBlur={onBlur}
label='Password'
//onChange={onChange}
onChange={(e)=> setPassword(e.target.value)}
id='auth-login-v2-password'
error={Boolean(errors.password)}
type={showPassword ? 'text' : 'password'}
endAdornment={
<InputAdornment position='end'>
<IconButton
edge='end'
onMouseDown={e => e.preventDefault()}
onClick={() => setShowPassword(!showPassword)}
>
{showPassword ? <EyeOutline /> : <EyeOffOutline />}
</IconButton>
</InputAdornment>
}
/>
)}
/>
{errors.password && (
<FormHelperText sx={{ color: 'error.main' }} id=''>
{errors.password.message}
</FormHelperText>
)}
</FormControl>
<Box
sx={{ mb: 4, display: 'flex', alignItems: 'center', flexWrap: 'wrap', justifyContent: 'space-between' }}
>
<FormControlLabel control={<Checkbox />} label='Remember Me' />
<Link passHref href='/forgot-password'>
<LinkStyled>Forgot Password?</LinkStyled>
</Link>
</Box>
<Button fullWidth size='large' type='submit' variant='contained' sx={{ marginBottom: 7 }}>
Login
</Button>
<Box sx={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', justifyContent: 'center' }}>
<Typography variant='body2' sx={{ marginRight: 2 }}>
New on our platform?
</Typography>
<Typography variant='body2'>
<Link passHref href='/register'>
<LinkStyled>Create an account</LinkStyled>
</Link>
</Typography>
</Box>
<Divider sx={{ my: 5 }}>or</Divider>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Link href='/' passHref>
<IconButton component='a' onClick={e => e.preventDefault()}>
<Facebook sx={{ color: '#497ce2' }} />
</IconButton>
</Link>
<Link href='/' passHref>
<IconButton component='a' onClick={e => e.preventDefault()}>
<Twitter sx={{ color: '#1da1f2' }} />
</IconButton>
</Link>
<Link href='/' passHref>
<IconButton component='a' onClick={e => e.preventDefault()}>
<Github
sx={{ color: theme => (theme.palette.mode === 'light' ? '#272727' : theme.palette.grey[300]) }}
/>
</IconButton>
</Link>
<Link href='/' passHref>
<IconButton component='a' onClick={e => e.preventDefault()}>
<Google sx={{ color: '#db4437' }} />
</IconButton>
</Link>
</Box>
</form>
</BoxWrapper>
</Box>
</RightWrapper>
</Box>
)
}
LoginPage.getLayout = page => <BlankLayout>{page}</BlankLayout>
LoginPage.guestGuard = true
export default LoginPage

AutoComplete: get the id for the user that i was choosen from users list, post request with status 400

I have this project and as it is clear in the postman, I have a request and through the request I must send the invoiceId to the invoice and the user ID must be sent in addition to a message, but the real problem is that I did not know how to get the user ID, the meaning is that when I printed “invoiceID” , "UserID "and "Message", the invoiceID and the message have a value, but UserID is worthless without value "undefined", and the reason is that I have list of the Users, and I must choose one user and then I want to pass this userID for the user that i was choosen and pass it in the "assignToUser" function, and i don't know how to do that.
And in the network I have these errors:
and in Network i have this errors:
enter image description here
how can i solve my problem?
invoiceSlice.js:
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import axios from "axios";
import FuseUtils from "#fuse/utils";
import { getInvoices } from "./invoicesSlice";
export const assignToUser = createAsyncThunk(
"invoicesApp/invoice/assignToUser",
async ({ invoiceId, userId, message }, { dispatch }) => {
console.log("invoiceId, userId, message", invoiceId, userId, message);
const response = await axios
.post(`/invoices/flow/${invoiceId}/approve`, { userId, message })
.catch((error) => {
console.log("error response: ", error);
});
const data = await response.data.data;
console.log("approve invoices: ", data);
dispatch(getInvoices());
return data;
}
);
const invoiceSlice = createSlice({
name: "invoicesApp/invoice",
initialState: null,
reducers: {
resetInvoice: () => null,
newInvoice: {
reducer: (state, action) => action.payload,
prepare: (event) => ({
payload: {
invoice: "",
netAmount: 0,
taxNumber: 0,
grossAmount: 0,
dueDate: "",
issueDate: "",
},
}),
},
},
extraReducers: {
[assignToUser.fulfilled]: (state, action) => action.payload,
},
});
export const { newInvoice, resetInvoice } = invoiceSlice.actions;
export default invoiceSlice.reducer;
approveUser.js:
import { Fragment, useState } from "react";
import { ButtonGroup } from "#material-ui/core";
import React from "react";
import Button from "#material-ui/core/Button";
import Dialog from "#material-ui/core/Dialog";
import DialogActions from "#material-ui/core/DialogActions";
import DialogContent from "#material-ui/core/DialogContent";
import DialogContentText from "#material-ui/core/DialogContentText";
import DialogTitle from "#material-ui/core/DialogTitle";
import useMediaQuery from "#material-ui/core/useMediaQuery";
import { useTheme } from "#material-ui/core/styles";
import { useSnackbar } from "notistack";
import Slide from "#material-ui/core/Slide";
import {
rejectInvoice,
approveInvoice,
assignToUser,
} from "../../store/invoiceSlice";
import { useDispatch, useSelector } from "react-redux";
import TextField from "#mui/material/TextField";
import FlagIcon from "#mui/icons-material/Flag";
import { makeStyles } from "#material-ui/core/styles";
import Autocomplete from "#mui/material/Autocomplete";
import { getUsers } from "../../store/invoiceSlice";
import { useEffect } from "react";
const useStyles = makeStyles((theme) => ({
paper: { padding: "3rem", maxWidth: "990px", minWidth: "300px" },
textStyle: {
paddingLeft: "2rem",
},
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
font: {
fontSize: "5rem",
},
}));
const GroupButttonApproveStatus = (id) => {
const [dialogOpen, setDialogOpen] = useState(false);
const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
const [assignToUserDialog, setAssignToUserDialog] = useState(false);
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
const dispatch = useDispatch();
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down("sm"));
const AssignToUserFullScreen = useMediaQuery(theme.breakpoints.down("md"));
const classes = useStyles();
const [users, setUsers] = useState([]);
const [message, setMessage] = useState("");
useEffect(() => {
getUsers().then((response) => {
setUsers(response);
});
}, []);
// confirm
console.log("users: ", users);
const handleAssignToUserDialogOpen = () => {
setAssignToUserDialog(true);
};
const handleAssignToUserDialogClose = () => setAssignToUserDialog(false);
// end assign to user
const handleConfirmDialogClose = () => setConfirmDialogOpen(false);
const handleClickConfirmDialogOpen = () => {
setConfirmDialogOpen(true);
};
//end confirm
const handleDialogClose = () => setDialogOpen(false);
const handleClickOpen = () => {
setDialogOpen(true);
};
const handleClose = () => {
setDialogOpen(false);
};
const rejectInvoiceHandleClick = () => {
enqueueSnackbar(
"Invoice rejected successfully",
{ variant: "error" },
{
anchorOrigin: {
vertical: "top",
horizontal: "right",
},
},
{ TransitionComponent: Slide }
);
};
const approveInvoiceHandleClick = () => {
enqueueSnackbar(
"Invoice approved successfully",
{ variant: "success" },
{
anchorOrigin: {
vertical: "top",
horizontal: "right",
},
},
{ TransitionComponent: Slide }
);
};
return (
<Fragment>
<ButtonGroup size="large">
<Button
onClick={(ev) => {
handleClickConfirmDialogOpen();
}}
>
Approve
</Button>
<Button
onClick={(ev) => {
handleClickOpen();
}}
>
Reject
</Button>
<Button
onClick={(ev) => {
handleAssignToUserDialogOpen();
}}
>
Assign to User to approve
</Button>
</ButtonGroup>
{/* reject Dialog */}
<Dialog
classes={{ paper: classes.paper }}
maxWidth="sm"
fullScreen={fullScreen}
open={dialogOpen}
onClose={handleDialogClose}
>
<DialogTitle style={{ fontWeight: "bold" }}>Reject Invoice</DialogTitle>
<DialogContent>
<div
style={{
backgroundColor: "#F8F9FA",
borderRadius: 10,
padding: "3rem",
}}
>
<DialogContentText>
<FlagIcon
style={{ fontSize: 40, color: "#dc3c24", paddingRight: "1rem" }}
/>
Do you really want to reject this invoice ?
</DialogContentText>
<DialogContentText>
<FlagIcon
style={{ fontSize: 40, color: "#F8F9FA", paddingRight: "1rem" }}
/>
Keep in mind that once the invoice is rejected you won’t be able
to proceed with it.
</DialogContentText>
</div>
</DialogContent>
<DialogActions>
<div style={{ paddingRight: "1rem" }}>
<Button
onClick={handleClose}
style={{ color: "#dc3c24", fontWeight: 500 }}
autoFocus
>
Cancel
</Button>
<Button
onClick={(ev) => {
dispatch(rejectInvoice(id?.id));
rejectInvoiceHandleClick(ev);
handleClose();
}}
style={{ color: "#212529", fontWeight: 500 }}
color="primary"
autoFocus
>
Reject Invoice
</Button>
</div>
</DialogActions>
</Dialog>
{/* End reject Dialog */}
{/* Confirm Dialog */}
<Dialog
classes={{ paper: classes.paper }}
maxWidth="sm"
fullScreen={fullScreen}
open={confirmDialogOpen}
onClose={handleConfirmDialogClose}
>
<DialogTitle style={{ fontWeight: "bold" }}>
Approve Invoice
</DialogTitle>
<DialogContent>
<div
style={{
backgroundColor: "#F8F9FA",
borderRadius: 10,
padding: "3rem",
}}
>
<DialogContentText>Almost ready for payment !</DialogContentText>
<DialogContentText>
By confirming you mark this invoice ready for approval.
</DialogContentText>
</div>
</DialogContent>
<DialogActions>
<div style={{ paddingRight: "1rem" }}>
<Button
onClick={handleConfirmDialogClose}
style={{ color: "#dc3c24", fontWeight: 500 }}
autoFocus
>
Cancel
</Button>
<Button
onClick={(ev) => {
dispatch(approveInvoice(id?.id));
approveInvoiceHandleClick(ev);
handleConfirmDialogClose();
}}
style={{ color: "#212529", fontWeight: 500 }}
color="primary"
autoFocus
>
yes, Confirm
</Button>
</div>
</DialogActions>
</Dialog>
{/* End Confirm Dialog */}
{/* assign to user dialog */}
<Dialog
classes={{ paper: classes.paper }}
maxWidth="sm"
fullScreen={AssignToUserFullScreen}
open={assignToUserDialog}
onClose={handleAssignToUserDialogClose}
>
<DialogTitle style={{ fontWeight: "bold", fontSize: "3rem" }}>
Request approval
</DialogTitle>
<div
style={{
backgroundColor: "#F8F9FA",
borderRadius: 10,
padding: "2rem",
paddingLeft: "2rem",
}}
>
<DialogContentText style={{ fontWeight: 600 }}>
{" "}
<FlagIcon
style={{ fontSize: 40, color: "#aacc00", paddingRight: "1rem" }}
/>
Send an invoice approval request to a team member.
</DialogContentText>
<DialogContentText style={{ paddingLeft: 10 }}>
The assigned member will receive a notification asking them to
approve this invoice. Once they accept, payment is on the way!
</DialogContentText>
</div>
<DialogTitle>Assign a member to approve</DialogTitle>
<DialogContent>
<Autocomplete
id="combo-box-demo"
// value={users || ""}
options={users || []}
getOptionLabel={(option) => option.name || ""}
sx={{ width: 860 }}
renderInput={(params) => (
<TextField
{...params}
placeholder="Search Member"
fullWidth
InputProps={{ ...params.InputProps, style: { fontSize: 17 } }}
InputLabelProps={{ style: { fontSize: 17 } }}
/>
)}
/>
</DialogContent>
<DialogContent style={{ marginTop: "15rem" }}>
<form className={classes.root} noValidate autoComplete="off">
<TextField
value={message}
onChange={(e) => setMessage(e.target.value)}
id="outlined-basic"
variant="outlined"
placeholder="Add a message"
fullWidth
size="medium"
InputProps={{ style: { fontSize: 17 } }}
InputLabelProps={{ style: { fontSize: 17 } }}
/>
</form>
</DialogContent>
<DialogActions>
<div style={{ paddingRight: "1rem" }}>
<Button
onClick={handleAssignToUserDialogClose}
style={{ color: "#dc3c24", fontWeight: 500 }}
autoFocus
>
Cancel
</Button>
<Button
onClick={(ev) => {
dispatch(assignToUser(id?.id, users.id, message));
approveInvoiceHandleClick(ev);
handleAssignToUserDialogClose();
}}
style={{ color: "#212529", fontWeight: 500 }}
color="primary"
autoFocus
>
Assign to approve
</Button>
</div>
</DialogActions>
</Dialog>
</Fragment>
);
};
export default GroupButttonApproveStatus;
invoiceDetails.js:
import React from "react";
import { getInvoice } from "../../store/invoiceSlice";
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import Grid from "#material-ui/core/Grid";
import TextField from "#material-ui/core/TextField";
import moment from "moment";
import InputAdornment from "#material-ui/core/InputAdornment";
import TodayIcon from "#material-ui/icons/Today";
import { makeStyles } from "#material-ui/core/styles";
import RejectDialog from "./rejectDialog";
import GroupButton from "./groupButttonReviewStatus";
import GroupButttonReviewStatus from "./groupButttonReviewStatus";
import GroupButttonApproveStatus from "./groupButtonApproveStatus";
import GroupButttonPaymentStatus from "./groupButtonPaymentStatus";
import useMediaQuery from "#material-ui/core/useMediaQuery";
import { useTheme } from "#material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
root: {
"& > *": {
margin: theme.spacing(1),
},
},
input: {
display: "none",
},
button: {
margin: theme.spacing(1),
// padding: theme.spacing(4),
},
}));
const InvoiceDetails = () => {
const classes = useStyles();
const theme = useTheme();
const routeParams = useParams();
const [invoice, setInvoice] = useState([]);
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef(null);
const [selectedIndex, setSelectedIndex] = React.useState(1);
const breakpoint = useMediaQuery(theme.breakpoints.down("sm"));
// const defaultLayoutPluginInstance = defaultLayoutPlugin();
useEffect(() => {
getInvoice(routeParams).then((response) => {
setInvoice(response);
});
}, []);
const handleClick = () => {
console.info(`You clicked ${options[selectedIndex]}`);
};
const handleMenuItemClick = (event, index) => {
setSelectedIndex(index);
setOpen(false);
};
const handleToggle = () => {
setOpen((prevOpen) => !prevOpen);
};
const handleClose = (event) => {
if (anchorRef.current && anchorRef.current.contains(event.target)) {
return;
}
setOpen(false);
};
console.log("invoice url: ", invoice?.file?.url);
console.log("invoice tara : ", invoice);
const statusGropButton = (status, id) => {
switch (status) {
case "review_pending":
return <GroupButttonReviewStatus id={id} />;
case "approval_pending":
return <GroupButttonApproveStatus id={id} />;
case "payment_pending":
return <GroupButttonPaymentStatus id={id} />;
case "rejected":
return <GroupButton id={id} />;
default:
return;
}
};
return (
<>
<Grid container>
<Grid item xs={7} sm={7}>
{/* pdf viewer */}
<object
// data={invoice?.file?.url}
data="https://documentcloud.adobe.com/view-sdk-demo/PDFs/Bodea%20Brochure.pdf"
type="application/pdf"
width="100%"
height="100%"
>
<p>
Alternative text - include a link{" "}
<a href="https://documentcloud.adobe.com/view-sdk-demo/PDFs/Bodea%20Brochure.pdf">
to the PDF!
</a>
</p>
</object>
</Grid>
<Grid item xs={5} sm={5} style={{ padding: "4rem" }}>
<Grid item>
<h1 style={{ fontWeight: "bold" }}>Invoice Details</h1>
</Grid>
<Grid item style={{ marginTop: "3rem", marginBottom: "2rem" }}>
<Grid item style={{ marginBottom: 10 }}>
<h3>From</h3>
</Grid>
<Grid item>
<h3>{invoice?.submittedBy?.name || ""}</h3>
</Grid>
<Grid item>
<h3>{invoice?.submittedBy?.email || ""}</h3>
</Grid>
</Grid>
<Grid item>
<Grid container item direction={breakpoint ? "row" : "column"}>
<Grid
container
item
xs={3}
sm={3}
direction="row"
justifyContent="flex-start"
alignItems="center"
>
<h3>Invoice ID</h3>
</Grid>
<Grid item xs={12} sm={12}>
<TextField
className="mt-8 mb-16"
id="outlined-size-normal"
value={invoice.id || ""}
variant="outlined"
fullWidth
/>
</Grid>
</Grid>
<Grid container item direction={breakpoint ? "row" : "column"}>
<Grid
container
item
xs={3}
sm={3}
direction="row"
justifyContent="flex-start"
alignItems="center"
>
<h3>Issue Date</h3>
</Grid>
<Grid item xs={12} sm={12}>
<TextField
className="mt-8 mb-16"
id="outlined-size-normal"
value={
moment(moment.utc(invoice.issueDate).toDate())
.local()
.format("YYYY-MM-DD HH:mm:ss") || ""
}
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment position="start">
<TodayIcon />
</InputAdornment>
),
}}
fullWidth
/>
</Grid>
</Grid>
<Grid container item direction={breakpoint ? "row" : "column"}>
<Grid
container
item
xs={3}
sm={3}
direction="row"
justifyContent="flex-start"
alignItems="center"
>
<h3>Due Date</h3>
</Grid>
<Grid item xs={12} sm={12}>
<TextField
className="mt-8 mb-16"
id="outlined-size-normal"
value={
moment(moment.utc(invoice.dueDate).toDate())
.local()
.format("YYYY-MM-DD HH:mm:ss") || ""
}
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment position="start">
<TodayIcon />
</InputAdornment>
),
}}
fullWidth
/>
</Grid>
</Grid>
<Grid container item direction={breakpoint ? "row" : "column"}>
<Grid
container
item
xs={3}
sm={3}
direction="row"
justifyContent="flex-start"
alignItems="center"
>
<h3>Net Amount</h3>
</Grid>
<Grid item xs={12} sm={12}>
<TextField
className="mt-8 mb-16"
id="outlined-size-normal"
value={invoice.netAmount || ""}
variant="outlined"
fullWidth
/>
</Grid>
</Grid>
<Grid container item direction={breakpoint ? "row" : "column"}>
<Grid
container
item
xs={3}
sm={3}
direction="row"
justifyContent="flex-start"
alignItems="center"
>
<h3>Tax Number</h3>
</Grid>
<Grid item xs={12} sm={12}>
<TextField
className="mt-8 mb-16"
id="outlined-size-normal"
value={invoice.taxNumber || ""}
variant="outlined"
fullWidth
/>
</Grid>
</Grid>
<Grid container item direction={breakpoint ? "row" : "column"}>
<Grid
container
item
xs={3}
sm={3}
direction="row"
justifyContent="flex-start"
alignItems="center"
>
<h3>Gross Amount</h3>
</Grid>
<Grid item xs={12} sm={12}>
<TextField
className="mt-8 mb-16"
// label="Size"
id="outlined-size-normal"
value={invoice.grossAmount || ""}
variant="outlined"
fullWidth
/>
</Grid>
</Grid>
<Grid
container
direction="row"
justifyContent="center"
alignItems="center"
style={{ marginTop: "3rem" }}
>
<Grid item>{statusGropButton(invoice.status, invoice?.id)}</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
</>
);
};
export default InvoiceDetails;
and this code:
useEffect(() => {
getUsers().then((response) => {
setUsers(response);
});
}, []);
return:
Based on your code I observed this.
You are already passing invoice?.id in statusGropButton(invoice.status, invoice?.id) method.
You are again expecting id from the input as id?.id in assignToUser(id?.id, users.id, message). Why?
Just assuming, because of that the invoiceId is going as undefined. Try by passing just id instead of id?.id while calling the api functions.
You are opening a Dialogue by using assignToUserDialog flag. While submitting the form, you are doing dispatch(assignToUser(id?.id, users.id, message)); in which users is an array. That is the reason you are getting undefined. I would suggest you maintain the specific user information for which the dialogue is opened and send that specifc user.id to the API call.
invoiceId undefined when send request ,you could check assignToUser

How render a list of options with renderOption in material UI

I want to change the background colour of the options inside an Autocomplete component, and the closest I can get is by using the renderOption prop.
The problem is that I can't figure out how to iterate (using map()) the options that I have in my state.
What I would like to do is something like
{state.myOptions.map( option => {
// here I would like to call renderOption = .....
}
Inside the <Autocomplete/> component
Is it possible to implement something like this or is there a well defined manner to do it?
EDIT
This is the component
import React, { useEffect } from 'react'
import { useForm, Form } from './hooks/useForm'
import EventIcon from '#material-ui/icons/Event';
import { makeStyles, TextField, Typography } from '#material-ui/core'
import CustomTextField from './inputs/CustomTextField';
import { Autocomplete } from '#material-ui/lab';
import { connect } from 'react-redux'
const EventForm = (props) => {
// Redux
const { family } = props
// React
const initialState = {
email: "",
password: "",
errors: {
email: "",
password: ""
},
familyMembers: ["rgeg"]
}
const { state, handleOnChange, setState } = useForm(initialState)
useEffect(() => {
family && state.familyMembers !== family.members && setState({
...state,
familyMembers: family.members
})
})
// Material UI
const useStyles = makeStyles(theme => (
{
message: {
marginTop: theme.spacing(3)
},
icon: {
backgroundColor: "lightgrey",
padding: "10px",
borderRadius: "50px",
border: "2px solid #3F51B5",
marginBottom: theme.spacing(1)
},
typography: {
marginBottom: theme.spacing(1),
marginTop: theme.spacing(4)
},
customTextField: {
marginTop: theme.spacing(0)
},
dateTimeWrapper: {
marginTop: theme.spacing(4)
}
}
))
const classes = useStyles()
return (
<>
<div>WORK IN PROGRESS...</div>
<br />
<br />
<EventIcon className={classes.icon} />
<Form
title="Add new event"
>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Enter a title for this event
</Typography>
<CustomTextField
className={classes.customTextField}
label="Title"
/>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Enter a location for this event
</Typography>
<CustomTextField
className={classes.customTextField}
label="Location"
/>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Which member/s of the family is/are attending
</Typography>
<Autocomplete
multiple
id="tags-outlined"
options={state.familyMembers}
getOptionLabel={(option) => option.name}
// defaultValue={[familyMembers[0]]}
filterSelectedOptions
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label="Members Attending"
placeholder="Family Member"
/>
)}
/>
</Form>
</>
);
}
// Redux
const mapStateToProps = (state) => {
return {
family: state.auth.family
}
}
export default connect(mapStateToProps)(EventForm);
If you only want to override the color of the option you can do it by overriding it's styles. No need to make custom option rendering function.
Above is the example of how can you achieve that.
import React, { useEffect } from 'react'
import { useForm, Form } from './hooks/useForm'
import EventIcon from '#material-ui/icons/Event';
import { makeStyles, TextField, Typography } from '#material-ui/core'
import CustomTextField from './inputs/CustomTextField';
import { Autocomplete } from '#material-ui/lab';
import { connect } from 'react-redux'
const EventForm = (props) => {
// Redux
const { family } = props
// React
const initialState = {
email: "",
password: "",
errors: {
email: "",
password: ""
},
familyMembers: ["rgeg"]
}
const { state, handleOnChange, setState } = useForm(initialState)
useEffect(() => {
family && state.familyMembers !== family.members && setState({
...state,
familyMembers: family.members
})
})
// Material UI
const useStyles = makeStyles(theme => (
{
message: {
marginTop: theme.spacing(3)
},
icon: {
backgroundColor: "lightgrey",
padding: "10px",
borderRadius: "50px",
border: "2px solid #3F51B5",
marginBottom: theme.spacing(1)
},
typography: {
marginBottom: theme.spacing(1),
marginTop: theme.spacing(4)
},
customTextField: {
marginTop: theme.spacing(0)
},
dateTimeWrapper: {
marginTop: theme.spacing(4)
},
option: {
backgroundColor: 'red'
}
}
))
const classes = useStyles()
return (
<>
<div>WORK IN PROGRESS...</div>
<br />
<br />
<EventIcon className={classes.icon} />
<Form
title="Add new event"
>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Enter a title for this event
</Typography>
<CustomTextField
className={classes.customTextField}
label="Title"
/>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Enter a location for this event
</Typography>
<CustomTextField
className={classes.customTextField}
label="Location"
/>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Which member/s of the family is/are attending
</Typography>
<Autocomplete
multiple
id="tags-outlined"
classes={{
option: classes.option
}}
options={state.familyMembers}
getOptionLabel={(option) => option.name}
// defaultValue={[familyMembers[0]]}
filterSelectedOptions
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label="Members Attending"
placeholder="Family Member"
/>
)}
/>
</Form>
</>
);
}
// Redux
const mapStateToProps = (state) => {
return {
family: state.auth.family
}
}
export default connect(mapStateToProps)(EventForm);
Wow, this took a while but the solution seems to use 'renderTags' algong with
here is the exact solution
import React, { useEffect } from 'react'
import { useForm, Form } from './hooks/useForm'
import EventIcon from '#material-ui/icons/Event';
import { makeStyles, TextField, Typography } from '#material-ui/core'
import CustomTextField from './inputs/CustomTextField';
import { Autocomplete } from '#material-ui/lab';
import { connect } from 'react-redux'
import Chip from '#material-ui/core/Chip';
import getColorvalue from './outputs/ColorValues'
const EventForm = (props) => {
// Redux
const { family } = props
// React
const initialState = {
email: "",
password: "",
errors: {
email: "",
password: ""
},
familyMembers: ["rgeg"]
}
const { state, handleOnChange, setState } = useForm(initialState)
useEffect(() => {
family && state.familyMembers !== family.members && setState({
...state,
familyMembers: family.members
})
})
// Material UI
const useStyles = makeStyles(theme => (
{
message: {
marginTop: theme.spacing(3)
},
icon: {
backgroundColor: "lightgrey",
padding: "10px",
borderRadius: "50px",
border: "2px solid #3F51B5",
marginBottom: theme.spacing(1)
},
typography: {
marginBottom: theme.spacing(1),
marginTop: theme.spacing(4)
},
customTextField: {
marginTop: theme.spacing(0)
},
dateTimeWrapper: {
marginTop: theme.spacing(4)
}
}
))
const classes = useStyles()
return (
<>
<div>WORK IN PROGRESS...</div>
<br />
<br />
<EventIcon className={classes.icon} />
<Form
title="Add new event"
>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Enter a title for this event
</Typography>
<CustomTextField
className={classes.customTextField}
label="Title"
/>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Enter a location for this event
</Typography>
<CustomTextField
className={classes.customTextField}
label="Location"
/>
<Typography
variant="subtitle1"
className={classes.typography}
align="left">
Which member/s of the family is/are attending
</Typography>
<Autocomplete
multiple
id="tags-outlined"
renderTags={(value, getTagProps) =>
value.map((option, index) => (
<Chip
variant="outlined"
key={option}
style={{
backgroundColor: `${getColorvalue(state.familyMembers[state.familyMembers.indexOf(option)].color)}`,
color: "white"
}}
label={option.name}
onDelete={() => console.log("test")}
{...getTagProps({ index })}
/>
))
}
options={state.familyMembers}
getOptionLabel={(option) => option.name}
// defaultValue={[familyMembers[0]]}
filterSelectedOptions
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label="Members Attending"
placeholder="Family Member"
/>
)}
/>
</Form>
</>
);
}
// Redux
const mapStateToProps = (state) => {
return {
family: state.auth.family
}
}
export default connect(mapStateToProps)(EventForm);

Resources