How can we validate our form using formik - reactjs

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.

Related

Formik TextFields are not updated once component is rendered

I'm fetching data from the server once component mounts, when I console.log(data) browser shows proper data.
I want to use Formik and MIU TextFields to display data which later on will be updated
const [data, setData] = useState([
{
action: "",
description: "",
tag: "",
category: "",
},
]);
console.log(data);
const [initialValues, setInitialValues] = useState([
{
action: "",
description: "",
tag: "",
category: "",
},
]);
useEffect(() => {
getData();
}, []);
useEffect(() => {
setInitialValues(data);
}, [data]);
const { values, handleChange, handleSubmit, resetForm } = useFormik({
initialValues: initialValues,
validationSchema,
onSubmit: () => {
console.log(values);
axios({
method: "POST",
url: "http://localhost:5000/allActions",
values,
}).then((response) => {
console.log(response);
});
resetForm();
},
});
This is Data component which displays TextFields
const Data = () => {
return (
<form onSubmit={handleSubmit}>
{!data[0].action && "...Loading"}
{data &&
data.map((element, idx) => (
<div key={idx} style={{ marginBottom: "100px" }}>
<Box sx={{ margin: "40px", padding: "20px" }}>
<TextField
variant="outlined"
label="action"
id={`action-${idx}`}
name={`values[${idx}].action`}
value={values[idx]?.action}
onChange={handleChange}
/>
</Box>
<Box sx={{ margin: "40px", padding: "20px" }}>
<TextField
variant="outlined"
label="Description"
id={`desciption-${idx}`}
name={`values[${idx}].description`}
value={values[idx]?.description}
onChange={handleChange}
/>
</Box>
<Box sx={{ margin: "40px", padding: "20px" }}>
<TextField
variant="outlined"
label="tag"
id={`id-${idx}`}
name={`values[${idx}].tag`}
value={values[idx]?.tag}
onChange={handleChange}
/>
</Box>
<Box sx={{ margin: "40px", padding: "20px" }}>
<TextField
variant="outlined"
label="category"
id={`category-${idx}`}
name={`values[${idx}].category`}
value={values[idx]?.category}
onChange={handleChange}
/>
</Box>
</div>
))}
<Button color="primary" variant="contained" type="submit">
Update
</Button>
</form>
);
};
The outcome of above code is that is displays proper amount of TextFields, in this case 3 blocks of 4 TextFields, but it's all empty, none of TextField have filled data which is displayed in console.
Can You please suggest what is wrong that TextFields are not updated ?

Disable button on Form Submit in React

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>

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

Problem autoFocus Autocomplete Material UI

I am not able to focus on the first element of my form which is an Autocomplete component of the Material UI.
When opening the modal, the focus is on the 'unit' element, as shown in the image below:
It dont's work
I want the focus to be on the 'group' element, as shown below:
It work
Here is the Sandbox link:
https://codesandbox.io/s/awesome-bassi-zjp50?file=/src/MyForm.jsx:1422-1431
can anyone help me?
MyForm
import React from "react";
import { Formik, Form, Field } from "formik";
import * as Yup from "yup";
import { Grid } from "#material-ui/core";
import Input from "./components/Input";
import Autocomplete from "./components/Autocomplete";
const initialValues = {
id: 0,
group: {
value: 0,
label: ""
},
name: "",
unit: {
value: 0,
label: ""
}
};
const groupList = [
{
value: "1",
label: "Seeds"
},
{
value: "2",
label: "Fertilizers"
}
];
const unitList = [
{
value: "1",
label: "kg"
},
{
value: "2",
label: "t"
}
];
const validationSchema = Yup.object({
group: Yup.object().shape({
value: Yup.number().required().min(1).nullable()
}),
name: Yup.string().required().max(50),
unit: Yup.object().shape({
value: Yup.number().required().min(1).nullable()
})
});
export default function MyForm() {
function _onSubmit(fields, { props, setErrors, setSubmitting }) {
console.log(fields);
}
return (
<Formik
enableReinitialize={true}
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={_onSubmit}
>
{function Render({ errors, touched, isSubmitting, setFieldValue }) {
return (
<Form id="form">
<Grid container direction="row">
<Grid item xs={12}>
<Field
name="group"
autoFocus
component={Autocomplete}
label="Group"
options={groupList}
disabled={isSubmitting}
textFieldProps={{
fullWidth: true,
margin: "dense",
variant: "outlined"
}}
/>
</Grid>
<Grid item xs={12}>
<Field
name="name"
fullWidth
label="Name"
disabled={isSubmitting}
component={Input}
/>
</Grid>
<Grid item xs={12}>
<Field
name="unit"
component={Autocomplete}
label="Unit"
options={unitList}
disabled={isSubmitting}
textFieldProps={{
fullWidth: true,
margin: "dense",
variant: "outlined"
}}
/>
</Grid>
</Grid>
</Form>
);
}}
</Formik>
);
}
Popup Modal
import React from "react";
import {
Dialog,
DialogTitle,
DialogContent,
makeStyles,
Typography,
Divider,
DialogActions,
Button,
Grid,
Box
} from "#material-ui/core";
const useStyles = makeStyles((theme) => ({
dialogWrapper: {
padding: theme.spacing(2),
position: "absolute",
top: theme.spacing(5)
},
dialogTitle: {
paddingRight: 0
},
button: {
margin: theme.spacing(0.5),
textTransform: "none"
},
buttonsContainer: {
display: "flex",
flex: "0 0 auto",
justifyContent: "flex-end",
width: "100%",
height: "100%"
},
buttons: {
display: "flex"
},
loadingIcon: {
display: "flex",
alignItems: "center",
justifyContent: "center",
marginRight: theme.spacing(1)
}
}));
function Popup(props) {
const classes = useStyles();
const { title, children, openPopup, setOpenPopup, ...rest } = props;
return (
<Dialog
{...rest}
open={openPopup}
maxWidth="sm"
classes={{ paper: classes.dialogWrapper }}
>
<DialogTitle dividers="true" className={classes.dialogTitle}>
<div style={{ display: "flex" }}>
<Typography variant="h4" component="div" style={{ flexGrow: 1 }}>
{title}
</Typography>
</div>
</DialogTitle>
<Divider />
<DialogContent {...rest} direction="row">
{children}
</DialogContent>
<DialogActions {...rest}>
<Grid {...rest} container className={classes.buttonsContainer}>
<Box {...rest} className={classes.buttons}>
<Button
className={classes.button}
variant="contained"
color="primary"
type="submit"
form="form"
{...rest}
>
Save
</Button>
<Button
className={classes.button}
color="default"
fullWidth
onClick={() => {
setOpenPopup(false);
}}
{...rest}
>
Cancel
</Button>
</Box>
</Grid>
</DialogActions>
</Dialog>
);
}
export default Popup;
Autocomplete
import React from "react";
import { FieldProps, getIn } from "formik";
import { TextField, CircularProgress } from "#material-ui/core";
import MuiAutocomplete, {
createFilterOptions
} from "#material-ui/lab/Autocomplete";
const NewAutocomplete: React.FC<
FieldProps & {
label?: string,
options: Array<{ label: string, value: number }>
}
> = ({ textFieldProps, field, form, label, options, isLoading, ...props }) => {
const filterOptions = createFilterOptions({
matchFrom: "start",
limit: 500
});
const errorText =
getIn(form.touched, field.name) && getIn(form.errors, field.name);
const valueInit = [
{
value: 0,
label: ""
}
];
return (
<MuiAutocomplete
{...props}
{...field}
filterOptions={filterOptions}
options={[...valueInit, ...options]}
getOptionLabel={(option) => (option ? option.label : "")}
getOptionSelected={(option, value) => option.value === value?.value}
style={{ width: 300 }}
loading={isLoading}
value={field.value}
onChange={(e, value) => {
form.setFieldValue(field.name, value);
}}
renderInput={(props) => (
<>
<TextField
{...props}
{...textFieldProps}
label={label}
helperText={errorText?.value || errorText}
error={!!errorText}
autoFocus
InputProps={{
...props.InputProps,
endAdornment: (
<React.Fragment>
{isLoading ? (
<CircularProgress color="primary" size={20} />
) : null}
{props.InputProps.endAdornment}
</React.Fragment>
)
}}
/>
</>
)}
/>
);
};
export default NewAutocomplete;
Thanks for your help!
You're actually setting autoFocus to true on TextField by default inside your NewAutocomplete definition (src/components/Autocomplete.jsx:50 in your sandbox).
Once that line removed, you can then add autoFocus: true to textFieldProps value of your first Field in MyForm component.
See codesandbox example

How to disable formik form after submission of form?

I have created a formik form that stores values at the backend. I have set-reset button, initial values. Everything working fine. But I can't able to find a way to disable formik form after submission of the form i.e if the user fills the form after a click on submit button user is not able to click on the text field.
// #ts-ignore
import React, { useState, useEffect } from 'react';
import { Formik, Form, Field } from 'formik';
import { Typography, Button, Grid } from '#material-ui/core';
import * as Yup from 'yup';
import { MyInput } from './comman/MyInput';
import { Textarea } from './comman/Textarea';
import { Event } from '../Tracking/Event';
const contactSchema = Yup.object().shape({
name: Yup.string().required('Please Enter Your Name'),
email: Yup.string()
.email('Please enter a valid email address')
.required('Please enter an email address'),
number: Yup.string()
.min(10, 'Must be 10 digit.')
.max(10, 'Must be 10 digit.'),
});
export default function ContactUs(props: any) {
const [submitted,setSubmittde] = useState(false);
const [state, setState] = useState({
msg: '',
});
const [selectedemail, setEmail] = useState({
text: '',
});
const [comname, setComname] = useState({
cName: '',
});
useEffect(() => {
fetch(`${window.location.href}`);
//const findemail = window.location.href;
const findemail =
'public/home';
const email =
findemail.match(/([a-zA-Z0-9._-]+#[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/) ||
[];
const finalemail = email[0].toString();
const company = findemail.match(/(?<=%)\w+(?=&)/g) || [];
const finalcompany = company.toString();
setComname({ cName: finalcompany });
setEmail({ text: finalemail });
}, []);
return (
<div>
<img
src="public/logo"
width="200"
className="d-inline-block"
alt="logo"
style={{
marginTop: '2em',
marginBottom: '2em',
float: 'right',
}}
/>
<Formik
enableReinitialize
initialValues={{
name: '',
email: selectedemail.text,
number: '',
companyname: comname.cName,
message: '',
}}
validationSchema={contactSchema}
onSubmit={(values, { resetForm }) => {
const data = {
name: values.name,
email: values.email,
number: values.number,
companyname: values.companyname,
message: values.message,
formtype: 'Email Marketing Contactus',
};
const request = new Request(
`${process.env.REACT_APP_PRODUCTION_EMAILCONTACTUS_API}`,
{
method: 'POST',
headers: new Headers({
'Content-Type': 'application/json',
}),
body: JSON.stringify(data),
},
);
fetch(request)
.then((res) => res.json())
.then((data) => {
if (
data.message ===
'We have received your message and will get in touch shortly. Thanks!'
) {
setState({
msg: data.message,
});
} else {
console.log('error');
}
});
resetForm();
}}
>
{({ errors, touched }) => (
<Grid container>
<Grid item xs={12}>
<Typography
variant="body1"
style={{
marginTop: '1em',
fontFamily: 'roboto',
fontSize: '1.3rem',
color: '#2F4858',
}}
>
To know more please submit your information, and one of our
expert advisors will get in touch within 24 hours!{' '}
</Typography>
<br />
<Form>
<Typography
variant="h6"
style={{
fontFamily: 'roboto',
color: '#2F4858',
marginTop: '0.5em',
}}
>
Full Name
</Typography>
<Field
id="outlined-multiline-flexible"
type="text"
name="name"
placeholder="Name"
component={MyInput}
/>
<br />
{errors.name && touched.name ? (
<div className="text-danger" style={{ color: 'red' }}>
{errors.name}
</div>
) : null}
<Typography
variant="h6"
style={{ fontFamily: 'roboto', marginTop: '1em' }}
>
Email
</Typography>
<Field
type="email"
name="email"
placeholder="Email"
values={props.text}
style={{ color: 'white' }}
component={MyInput}
/>{' '}
<br />
{errors.email && touched.email ? (
<div className="text-danger" style={{ color: 'red' }}>
{errors.email}
</div>
) : null}
<Typography
variant="h6"
style={{
fontFamily: 'roboto',
color: '#2F4858',
marginTop: '1em',
}}
>
Mobile Number (Optional)
</Typography>
<Field
name="number"
placeholder="Contact Number"
component={MyInput}
// label="Contact Number"
/>
<br />
{errors.number && touched.number ? (
<div className="text-danger" style={{ color: 'red' }}>
{errors.number}
</div>
) : null}
<Typography
variant="h6"
style={{
fontFamily: 'roboto',
color: '#2F4858',
marginTop: '1em',
}}
>
Company Name (Optional)
</Typography>
<Field
name="companyname"
placeholder="Company Name"
// label="Contact Number"
component={MyInput}
/>
<br />
<Typography
variant="h6"
style={{
fontFamily: 'roboto',
color: '#2F4858',
marginTop: '1em',
}}
>
How can we help? (Optional)
</Typography>
<Field
id="outlined-multiline-static"
name="message"
placeholder="Your question about our services"
label="How can we help?"
component={Textarea}
/>
<br />
<br />
<Button
type="submit"
variant="contained"
onClick={() =>
Event(
'Contact from',
'Client submitted ',
'domain',
)
}
style={{
background: '#F79924',
color: 'white',
fontFamily: 'roboto',
fontSize: '1rem',
}}
>
Submit{' '}
</Button>
<br />
{state.msg && (
<Typography
variant="h6"
style={{
color: '#4BB543',
fontFamily: 'roboto-medium',
marginTop: '1em',
}}
>
{' '}
{state.msg}{' '}
</Typography>
)}
</Form>
</Grid>
</Grid>
)}
</Formik>
</div>
);
}
[![Formik form][1]][1]
I don't know how to disable formik form. Please help?
[1]: https://i.stack.imgur.com/PQXBV.png
This can be simply done using the disabled attribute and your existing value isSubmitted.
Pass a prop disabled into your custom input:
<Field
type="email"
name="email"
placeholder="Email"
values={props.text}
style={{ color: 'white' }}
component={MyInput}
disabled={isSubmitted}
/>
And apply it in the custom input using the ...props spread:
const MyInput = ({
field,
label,
name,
id,
value,
...props
}) => (
<div>
<label htmlFor="id">{label} </label>
<input id={id} type="text" {...field} {...props} />
</div>
);
And make sure you set isSubmitted somewhere in the onSubmit function
fetch(request)
.then((res) => res.json())
.then((data) => {
if (
data.message ===
'We have received your message and will get in touch shortly. Thanks!'
) {
setState({
msg: data.message,
});
setIsSubmitted(true);
} else {
console.log('error');
}
});
Live demo of a simple form that disables inputs/button after submission.

Resources