how to mask input fields onKeyUp using react material ui? - reactjs

I have created login form where it has DOB and PHONE fields. Where the user has to type it. While typing it should mask the values as DOB format (YYYY-MM-DD) and phone format as (999) 999-9999. I have used material ui for TextFied and react-hook-form for form validation. I can achieve some part by giving regex on KeyUp. But it accepts the letter in Number field. Here i added the example in
CodeSandbox : https://codesandbox.io/s/react-material-ui-login-tdcoh
And also i am shring the code here. please help me with this.
Thanks in advance
import React from "react";
import {Paper,withStyles,Grid,TextField,Button,FormControlLabel,Checkbox } from "#material-ui/core";
import { Face, Fingerprint } from "#material-ui/icons";
import { useForm } from "react-hook-form";
const styles = theme => ({
margin: {
margin: theme.spacing.unit * 2
},
padding: {
padding: theme.spacing.unit
}
});
const LoginTab = () => {
const { handleSubmit, register, errors, reset } = useForm({
mode: "onChange"
});
const onSubmit = values => {
alert(JSON.stringify(values));
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Grid container spacing={8} alignItems="flex-end">
<Grid item>
<Face />
</Grid>
<Grid item md={true} sm={true} xs={true}>
<TextField
id="dob"
label="DOB"
name="dob"
onKeyUp={e => {
var v = e.target.value;
if (v.match(/^\d{4}$/) !== null) {
e.target.value = v + "/";
} else if (v.match(/^\d{4}\/\d{2}$/) !== null) {
e.target.value = v + "/";
}
}}
inputRef={register({
required: "FIELD_REQUIRED_MESSAGE",
pattern: {
value: /(\d{2})-(\d{2})-(\d{4})/,
message: "Please enter a correct DOB pattern."
}
})}
/>
{errors.dob && <p>{errors.dob.message}</p>}
</Grid>
</Grid>
<Grid container spacing={8} alignItems="flex-end">
<Grid item md={true} sm={true} xs={true}>
<TextField
id="username"
label="PHONE"
name="phone"
onKeyUp={e => {
let x = e.target.value
.replace(/\D/g, "")
.match(/(\d{0,3})(\d{0,3})(\d{0,4})/);
e.target.value = !x[2]
? x[1]
: "(" + x[1] + ") " + x[2] + (x[3] ? "-" + x[3] : "");
}}
inputRef={register({
required: "FIELD_REQUIRED_MESSAGE",
pattern: {
value: /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/,
message: "PHONE_VALIDATION_PATTERN_MESSAGE"
}
})}
/>
{errors.phone && <p>{errors.phone.message}</p>}
</Grid>
</Grid>
<Grid container alignItems="center" justify="space-between">
<Grid item>
<FormControlLabel
control={<Checkbox color="primary" />}
label="Remember me"
/>
</Grid>
<Grid item>
<Button
disableFocusRipple
disableRipple
style={{ textTransform: "none" }}
variant="text"
color="primary"
>
Forgot password ?
</Button>
</Grid>
</Grid>
<Grid container justify="center" style={{ marginTop: "10px" }}>
<Button
variant="outlined"
color="primary"
type="submit"
style={{ textTransform: "none" }}
>
Login
</Button>
</Grid>
</form>
);
};
export default withStyles(styles)(LoginTab);

Related

Required property from React hook form does not validate on textfields

I'm currently using React-hook-form to manage my textfields and that includes validation.
The problem right now is that the required property does not work well, because it does not show the little helpertext under the inputfield when it is empty.
Here is there a snippet of the UI, it only shows a few of the inputfields to save you the
unnecessary JSX code
This is how i have done it:
import { FragmentType, useFragment } from '#gql/fragment-masking';
import { graphql } from '#gql/gql';
import { Gender } from '#gql/graphql';
import {
Button,
Card,
CardContent,
Grid,
MenuItem,
TextField,
TextFieldProps,
Typography,
} from '#mui/material';
import { AdapterDayjs } from '#mui/x-date-pickers/AdapterDayjs';
import { DesktopDatePicker } from '#mui/x-date-pickers/DesktopDatePicker';
import { LocalizationProvider } from '#mui/x-date-pickers/LocalizationProvider';
import dayjs, { Dayjs } from 'dayjs';
import { useTranslation } from 'next-i18next';
import React, { useCallback, useState } from 'react';
import { useForm } from 'react-hook-form';
type Inputs = {
firstName: string;
lastName: string;
birthdate: string;
street: string;
postalcode: string;
city: string;
country: string;
gender: string;
email: string;
leveloftrust: string;
lastsignedin: string;
};
export const PatientInfoFragment = graphql(/* GraphQL */ `
fragment PatientInfo on Patient {
addresses {
city
lines
postalCode
}
birthDate
email
gender
id
name {
firstName
lastName
}
status {
lastSignInAt
levelOfTrust
}
}
`);
interface PatientInfoProps {
patient: FragmentType<typeof PatientInfoFragment>;
}
export function PatientInfo(props: PatientInfoProps) {
const patient = useFragment(PatientInfoFragment, props.patient);
const [value, setValue] = React.useState<Dayjs | null>(
dayjs(patient.birthDate)
);
const genderValueArray = Object.values(Gender);
const [disabled, setDisabled] = useState(true);
const { register, handleSubmit } = useForm<Inputs>({
defaultValues: {
firstName: patient.name?.firstName ?? '',
lastName: patient.name?.lastName ?? '',
birthdate: patient.birthDate ?? '',
country: '',
gender: patient.gender,
email: patient.email ?? '',
leveloftrust: patient.status?.levelOfTrust,
lastsignedin: patient.status?.lastSignInAt,
postalcode: patient.addresses[0]?.postalCode ?? '',
city: patient.addresses[0]?.city ?? '',
street: patient.addresses[0]?.lines[0] ?? '',
},
shouldUseNativeValidation: true,
});
const handleChange = React.useCallback(
(newValue: Dayjs | null) => setValue(newValue),
[]
);
const { t } = useTranslation();
const handleEditClick = useCallback(() => setDisabled(!disabled), [disabled]);
const renderInputField = React.useCallback(
(params: JSX.IntrinsicAttributes & TextFieldProps) => {
return <TextField {...params} />;
},
[]
);
const onSubmit = (data: any) => console.log(data);
return (
<Card>
<CardContent>
<form onSubmit={handleSubmit(onSubmit)}>
<Grid container direction="row" justifyContent="space-between">
<Grid item>
<Typography gutterBottom variant="h5" component="div">
{t('patient.info.title', 'Personal Information')}
</Typography>
</Grid>
<Grid item>
<Grid container justifyContent="space-between" sx={{ m: 1 }}>
{!disabled && (
<Button
onClick={handleEditClick}
size="large"
variant="outlined">
Cancel
</Button>
)}
<Button
onClick={handleEditClick}
size="large"
type="submit"
variant="contained">
{!disabled
? t('patient.setToComplete', 'Set to complete')
: t('patient.edit', 'Edit')}
</Button>
</Grid>
</Grid>
</Grid>
<Grid container direction="row">
<Grid
container
sx={{ margin: 1 }}
rowSpacing={3}
spacing={{ md: 4 }}>
<Grid item>
<TextField
{...register('firstName', { required: 'Field needs to be filled out' })}
label="Name"
name="firstName"
id="component-outlined"
disabled={disabled}
/>
</Grid>
<Grid item>
<TextField
{...register('lastName', {
required: 'Field needs to be filled out',
})}
label="lastname"
name="lastName"
id="component-outlined"
disabled={disabled}
/>
</Grid>
<Grid item sx={{ width: '274.67px' }}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DesktopDatePicker
{...register('birthdate', {
required: 'Field needs to be filled out',
})}
label="Birthdate"
inputFormat="MM/DD/YYYY"
value={value}
onChange={handleChange}
renderInput={renderInputField}
disabled={disabled}
/>
</LocalizationProvider>
</Grid>
</Grid>
<Grid
container
sx={{ margin: 1 }}
rowSpacing={3}
spacing={{ md: 4 }}>
<Grid item>
<TextField
{...register('street')}
id="component-outlined"
label="Street"
name="street"
disabled={disabled}
/>
</Grid>
<Grid item>
<TextField
{...register('postalcode')}
id="component-outlined"
label="Postal code"
name="postalCode"
disabled={disabled}
/>
</Grid>
<Grid item>
<TextField
{...register('city')}
id="component-outlined"
label="City"
name="city"
disabled={disabled}
/>
</Grid>
</Grid>
<Grid
container
sx={{ margin: 1 }}
rowSpacing={3}
spacing={{ md: 4 }}>
<Grid item>
<TextField
{...register('country')}
id="component-outlined"
label="Country"
name="country"
disabled={disabled}
/>
</Grid>
<Grid item>
<TextField
sx={{ width: '242.67px' }}
id="outlined-select-gender"
select
{...register('gender')}
label="Gender"
disabled={disabled}
value={patient.gender}>
{genderValueArray.map(option => (
<MenuItem key={option} value={option}>
{option}
</MenuItem>
))}
</TextField>
</Grid>
<Grid item>
<TextField
{...register('email')}
id="component-outlined"
label="Email"
name="email"
disabled
/>
</Grid>
</Grid>
<Grid
container
sx={{ margin: 1 }}
rowSpacing={3}
spacing={{ md: 4 }}>
<Grid item>
<TextField
{...register('leveloftrust')}
id="component-outlined"
label="Level of trust"
name="leveloftrust"
disabled
/>
</Grid>
<Grid item>
<TextField
{...register('lastsignedin')}
id="component-outlined"
label="Last signed in"
name="lastsignedin"
disabled
/>
</Grid>
</Grid>
</Grid>
</form>
</CardContent>
</Card>
);
}
If you're using React Hook Form with MUI Textfield, you'll need to use the Controller component wrapper from react-hook-form for your MUI Textfield components.
View Integrating with UI Libraries
For example, you would grab the Input component from MUI's Core library instead:
import Input from "#material-ui/core/Input";
Then in your form you'd wrap it using Controller like:
<Controller
name="firstName"
control={control}
render={({ field }) => <Input {...field} />}
/>
Then MUI Input has has error boolean and helperText string as available properties on the Input component. For example using Formik (but any form library would work with MUI):
<TextField
id="password"
name="password"
label="Password"
type="password"
value={formik.values.password}
onChange={formik.handleChange}
error={!!formik.errors.password && formik.touched.password}
helperText={formik.touched.password && formik.errors.password}
required
/>
Otherwise, reviewing their documentation on how to Handle Errors, it appears with no library they have you handle error output manually using the errors object from formState. You'd destructure that data like:
const { register, formState: { errors }, handleSubmit } = useForm();
Finally, they link a very handy CodeSandbox which uses a #hookform/error-message library that can wrap regular HTML inputs. If you want to stick with MUI, I'd go with the helperText and error properties.

React Hook Form is not submitting

I'm new with react form, I'm using Material UI and Controller Component, and I'm sending a React Hook form request but not getting any response, form's (HTML form tag) onSubmit event is occurring but handleSubmit is not working I have one more form like that it is working perfectly fine but I don't know why it's not working, can anybody please help me with that
import { Button, useTheme, Grid, TextField, Typography } from '#mui/material';
import WSSelectBox from 'components/common/WSSelect';
import React, { FC } from 'react';
import { Controller, FormProvider, useForm } from 'react-hook-form';
import { isMobile } from 'utils/mediaqueries';
import CheckBoxButton from '../CheckBoxButton';
import { formTypes } from './utils';
import { yupResolver } from '#hookform/resolvers/yup';
import * as yup from 'yup';
import { FormPropTypes } from './type';
const schema = yup
.object()
.shape({
name: yup.string().required(),
residentialCountry: yup.number().required(),
initiator: yup.string().required(),
program: yup.string().required(),
list: yup.string().required(),
})
.required();
const Entities: FC<FormPropTypes> = ({
handleClick,
selectedType,
handleSearch,
}) => {
const theme = useTheme();
const methods = useForm({
resolver: yupResolver(schema),
});
const { handleSubmit, control } = methods;
const onSubmit = (data) => {
// Backend is not done yet
console.log('DataData', data);
};
return (
<FormProvider {...methods}>
<form
onSubmit={(e) => {
e.preventDefault();
console.log('skdjskd', 'Line no 44');
handleSubmit(onSubmit);
}}
>
<Grid container spacing={'16px'}>
{formTypes.map((type) => (
<Grid item xs={6} sm={'auto'} md={'auto'} key={type}>
<CheckBoxButton
key={type}
name={'type'}
value={type}
handleClick={handleClick}
active={type == selectedType ? true : false}
/>
</Grid>
))}
</Grid>
<Grid container pt={4} columnSpacing="20px" rowSpacing={'16px'}>
<Grid item xs={12} sm={12} md={6}>
<Controller
name="name"
render={({
// eslint-disable-next-line #typescript-eslint/no-unused-vars
field: { value, ...otherFieldProps },
fieldState,
}) => (
<TextField
fullWidth
id="sanctions-form-name"
label="Name"
variant="outlined"
helperText={
<p
style={{
position: 'relative',
left: -13,
fontSize: 12,
}}
>
Try to enter the full name or part of it. It is possible
to use original language or the Latin alphabet.
</p>
}
required
error={!!fieldState.error}
{...otherFieldProps}
/>
)}
/>
</Grid>
<Grid item xs={12} sm={6} md={3}>
<WSSelectBox
id="sanctions-form-residential-country"
label="Residential country"
name={'residentialCountry'}
data={['Ten', 'Twenty']}
handleSelect={() => {}}
type={'text'}
control={control}
/>
</Grid>
<Grid item xs={12} sm={6} md={3}>
<WSSelectBox
data={['Ten', 'Twenty']}
handleSelect={() => {}}
id="sanctions-form-initiator"
name={'initiator'}
label="Initiator"
type="text"
control={control}
/>
</Grid>
<Grid item xs={12} sm={6} md={6}>
<WSSelectBox
id="sanctions-form-program"
label="Program"
data={['Ten', 'Twenty']}
handleSelect={() => {}}
type="text"
name={'program'}
control={control}
/>
</Grid>
<Grid item xs={12} sm={6} md={6}>
<WSSelectBox
id="sanctions-form-list"
label="List"
data={['Ten', 'Twenty']}
handleSelect={() => {}}
type={'text'}
name={'list'}
control={control}
/>
</Grid>
</Grid>
<Grid
container
pt={{ xs: '20px', md: '30px' }}
rowSpacing="10px"
alignItems="center"
sx={{
[isMobile(theme)]: {
textAlign: 'center',
},
}}
justifyContent="left"
>
<Grid item xs={6} sm={'auto'}>
<Button
variant="contained"
color="primary"
sx={{
minWidth: '200px',
['#media (max-width:460px)']: {
minWidth: '120px',
},
}}
type="submit"
>
Search
</Button>
</Grid>
<Grid item xs={6} sm={'auto'}>
<Button
sx={{
minWidth: '200px',
color: '#3883FA',
['#media (max-width:460px)']: {
minWidth: '120px',
},
}}
type="submit"
>
Reset
</Button>
</Grid>
</Grid>
</form>
<Typography
variant="body2"
sx={{ fontSize: 17, color: '#121E3D', marginTop: '20px' }}
>
Use filters to start your search.
</Typography>
</FormProvider>
);
};
export default Entities;
SELECT BOX
import { TextField, MenuItem, styled, Select } from '#mui/material';
import { Controller, useForm } from 'react-hook-form';
export type SelectBoxProps = {
label: string;
id: string;
data: Array<string>;
handleSelect: VoidFunction;
type: string;
Control: () => {};
};
const SelectBox = styled(TextField)`
& .MuiOutlinedInput-root:focus {
&.Mui-focused fieldset {
border-bottom: none !important;
}
}
`;
const WSSelectBox = ({
label,
id,
data,
handleSelect,
name,
type,
control,
}) => {
return (
<>
<Controller
name={name}
render={({ field }) => (
<SelectBox
defaultValue=""
fullWidth
autoComplete="off"
id={id}
type={type}
label={label}
variant="outlined"
required
select
{...field}
>
{data.map((opt) => (
<MenuItem key={opt} value={opt}>
{opt}
</MenuItem>
))}
</SelectBox>
)}
control={control}
/>
</>
);
};
export default WSSelectBox;

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

form validation in material ui alongside Axios

I'm currently working on a register form using react, and using axios to connect with my Django Rest Framework API, but I got some issue with from validation, I look up many solutions but many of them use React class component, and those using react function component don't work with axios.
I could really use your help on any type of form validation that i could use, this is my current code
import React, { useState } from 'react';
import axiosInstance from '../../../network/axios';
// eslint-disable-next-line
import { useHistory } from 'react-router-dom';
import { Link } from 'react-router-dom';
//Material-Ui
import Avatar from '#material-ui/core/Avatar';
import Button from '#material-ui/core/Button';
import CssBaseline from '#material-ui/core/CssBaseline';
import TextField from '#material-ui/core/TextField';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
import Grid from '#material-ui/core/Grid';
import Box from '#material-ui/core/Box';
import LockOutlinedIcon from '#material-ui/icons/LockOutlined';
import Typography from '#material-ui/core/Typography';
import { makeStyles } from '#material-ui/core/styles';
import Container from '#material-ui/core/Container';
function Copyright() {
return (
<Typography variant="body2" color="textSecondary" align="center">
{'Copyright © '}
<Link color="inherit" href="https://material-ui.com/">
Your Website
</Link>{' '}
{new Date().getFullYear()}
{'.'}
</Typography>
);
}
const useStyles = makeStyles((theme) => ({
paper: {
marginTop: theme.spacing(8),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
},
avatar: {
margin: theme.spacing(1),
// backgroundColor: theme.palette.secondary.main,
backgroundColor: 'linear-gradient(62deg, #bbbbbb 0%, #6b6868 100%)',
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(3),
},
submit: {
margin: theme.spacing(3, 0, 2),
// background: 'linear-gradient(62deg, #bbbbbb 0%, #6b6868 100%)',
background:'linear-gradient(90deg, rgba(9,42,121,1) 0%, rgba(11,161,163,1) 35%, rgba(0,212,255,1) 80%)',
// background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
borderRadius: 3,
border: 0,
color: 'white',
height: 48,
padding: '0 30px',
boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
},
}));
export default function SignUp() {
//histoary to make a redirect to
// eslint-disable-next-line
const history = useHistory();
const initialFormData = Object.freeze({
email: '',
first_name: '',
last_name: '',
password1: '' ,
password2: '' ,
});
const [formData, updateFormData] = useState(initialFormData);
const handleChange = (e) => {
updateFormData({
...formData,
// Trimming any whitespace
[e.target.name]: e.target.value.trim(),
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(formData);
axiosInstance
.post(`users/create/`, {
email: formData.email,
first_name: formData.first_name ,
last_name: formData.last_name ,
password1: formData.password1 ,
password2: formData.password2 ,
})
.then((res) => {
history.push('/welcome');
console.log(res);
console.log(res.data);
});
}
const classes = useStyles();
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign up
</Typography>
<form className={classes.form} noValidate>
<Grid container spacing={2}>
<Grid item xs={12} sm={6}>
<TextField
autoComplete="fname"
name="first_name"
variant="outlined"
required
fullWidth
id="first_name"
label="First Name"
autoFocus
onChange={handleChange}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
variant="outlined"
required
fullWidth
id="last_name"
label="Last Name"
name="last_name"
autoComplete="lname"
onChange={handleChange}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
onChange={handleChange}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
name="password1"
label="Password"
type="password"
id="password1"
autoComplete="current-password1"
onChange={handleChange}
/>
</Grid>
<Grid item xs={12}>
<TextField
variant="outlined"
required
fullWidth
name="password2"
label="Password"
type="password"
id="password2"
autoComplete="current-password2"
onChange={handleChange}
/>
</Grid>
<Grid item xs={12}>
<FormControlLabel
control={<Checkbox value="allowExtraEmails" color="primary" />}
label="I want to receive inspiration, marketing promotions and updates via email."
/>
</Grid>
</Grid>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
onClick= {handleSubmit}
>
Sign Up
</Button>
</form>
<Grid container justify="flex-end">
<Grid item>
<Button hcomponent={Link} to="/logins" variant="body2">
Already have an account? Sign in
</Button>
</Grid>
</Grid>
</div>
<Box mt={5}>
<Copyright />
</Box>
</Container>
);
}

how to preview the selected image upload React js

Have created a button called image upload, need to view the selected image when selected
<GridItem xs={6}>
<FormControl className={classes.formControl}>
<input
accept="image/*"
className={classes.input}
style={{ display: 'none' }}
id="raised-button-file"
multiple
type="file"
/>
<label htmlFor="raised-button-file">
<Button
variant="raised"
component="span"
className={classes.button}
>
Upload Image
</Button>
</label>
</FormControl>
</GridItem>
you can set the file object URL in the state during the onChange and use it in an img tag like this,
const [file, setFile] = useState(undefined);
const handleChange = (event) => {
setFile(URL.createObjectURL(event.target.files[0]));
}
use this handleChange method in input onChange event,
<GridItem xs={6}>
<FormControl className={classes.formControl}>
<input
accept="image/*"
className={classes.input}
style={{ display: 'none' }}
id="raised-button-file"
multiple
type="file"
onChange={handleChange}
/>
{/* preview of file */}
{ file && <img src={this.state.file}/>}
<label htmlFor="raised-button-file">
<Button
variant="raised"
component="span"
className={classes.button}
>
Upload Image
</Button>
</label>
</FormControl>
</GridItem>
also if you want to allow the user to crop the image selected there is this library react-image-crop. I use it and it works pretty good for me.
before select
select image
Typescript
import { Grid, IconButton, makeStyles, Tooltip } from '#material-ui/core'
import { PhotoCamera, Publish } from "#material-ui/icons"
import React, { useState } from 'react'
import { useDispatch } from 'react-redux'
import { IRequestState } from '../../assets/interfaces'
const FILE = 'ImageUpload.tsx'
const useStyles = makeStyles((theme) => ({
root: {
'& > *': {
margin: theme.spacing(1),
},
}, right: {
float: "right"
}, w100: {
width: "100%"
}, input: {
display: "none",
},
faceImage: {
color: theme.palette.primary.light,
}, success: {
color: theme.palette.success.main
}
}))
interface IImageUploadProps {
Submit(event: React.MouseEvent<HTMLButtonElement>): void
}
type IImageUploadState = IRequestState & {
imageFile?: any
}
export default function ImageUpload(props: IImageUploadProps) {
const classes = useStyles()
const dispatch = useDispatch()
const initState: IImageUploadState = { inited: false, requesting: false }
const [values, setState] = useState<IImageUploadState>(initState)
const handleCapture = ({ target }: any) => {
if (target && target.files && target.files.length)
setState({ ...values, imageFile: target.files[0] })
}
return (
<>
<Grid
container
direction="row"
justify="space-evenly"
alignItems="center"
spacing={2}
>
<Grid item sm={6} md={3}>
<Grid
justify="center"
alignItems="center"
container
>
<Grid item>
<input
accept="image/jpeg"
className={classes.input}
id="faceImage"
type="file"
onChange={handleCapture}
/>
<Tooltip title="select image">
<label htmlFor="faceImage">
<IconButton
className={classes.faceImage}
color="primary"
aria-label="upload picture"
component="span"
>
<PhotoCamera fontSize="large" />
</IconButton>
</label>
</Tooltip>
</Grid>
</Grid>
</Grid>
<Grid item sm={6} md={3}>
<img className={classes.w100} hidden={!values.imageFile} src={values.imageFile && URL.createObjectURL(values.imageFile)} alt="Logo" />
</Grid>
<Grid item sm={6} md={3}>
<Grid
justify="center"
alignItems="center"
container
>
<Grid item>
<Tooltip title="upload image">
<IconButton
className={classes.success}
aria-label="upload"
onClick={props.Submit}
edge="end"
disabled={!values.imageFile}
>
<Publish />
</IconButton>
</Tooltip>
</Grid>
</Grid>
</Grid>
</Grid>
</>
)
}

Resources