I am trying to post product data using axios. When I submit my datas entered in the form, I see this error in the console : AxiosError {message: 'Request failed with status code 400', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …}
This is my React Component code for adding new product, I made a validation for datas using Formik and Yup, my form contains TextFields and Material UI DatePicker, I cast The Date Format to dayjs().
import {React} from 'react';
import {useNavigate} from 'react-router-dom';
import { useAuthContext } from '../../../hooks/useAuthContext';
import axios from 'axios';
import dayjs from 'dayjs';
/**************|| Validation : Formik & Yup ||**************/
import { useFormik } from 'formik';
import * as Yup from 'yup';
/**************|| Material UI ||**************/
import {Grid, Paper, Typography, TextField, Button} from '@mui/material';
import {DemoContainer} from '@mui/x-date-pickers/internals/demo';
import {LocalizationProvider} from '@mui/x-date-pickers/LocalizationProvider';
import {AdapterDayjs} from '@mui/x-date-pickers/AdapterDayjs';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import InputAdornment from '@mui/material/InputAdornment';
/**************|| React-icons ||**************/
import { FaGift , FaCartPlus} from "react-icons/fa";
import {MdDescription, MdAddLocation} from "react-icons/md";
import {RiBarcodeBoxLine} from "react-icons/ri";
/************************************************************************************************************/
// Validation Schema for add product fields
const validationSchema = Yup.object({
productName: Yup.string()
.required("Le nom du produit est requis.")
.matches(/^[a-zA-Z0-9À-ÿ ]+$/, "Le nom du produit doit être alphanumérique."),
description: Yup.string()
.required("La description est requise.")
.matches(/^[a-zA-Z0-9À-ÿ ]+$/, "La description doit être alphanumérique."),
manufacturingDate: Yup.date().nullable()
.typeError("La date de fabrication est requise.")
.required("La date de fabrication est requise."),
expiryDate: Yup.date().nullable()
.typeError("La date d'expiration est requise.")
.when("manufacturingDate",
(manufacturingDate, Yup) => manufacturingDate && Yup.min(manufacturingDate, "La date d'expiration doit être après la date de fabrication."))
.required("La date d'expiration est requise."),
batchNumber: Yup.string()
.required("Le numéro de lot est requis.")
.matches(/^[a-zA-Z0-9À-ÿ ]+$/, "Le numéro de lot doit être alphanumérique."),
quantity: Yup.number()
.required("La quantité est requise.")
.positive("La quantité doit être supérieure à zéro.")
.integer("La quantité doit être un nombre entier."),
location: Yup.string()
.required("L'emplacement est requis.")
.matches(/^[a-zA-Z0-9À-ÿ ]+$/, "L'emplacement doit être alphanumérique."),
});
const NewProduct = () => {
// styles
const paperStyle = { padding: '30px 20px', margin: "20px auto" }
const headerStyle = { margin: 0, fontWeight:"800" }
const marginTop = { marginTop: 15 }
/************************************************************************************************************/
const navigate = useNavigate();
const { user } = useAuthContext();
// initial values for form fields
const formik = useFormik({
initialValues: {
productName: "",
description: "",
manufacturingDate: dayjs(),
expiryDate: dayjs(),
batchNumber: "",
quantity: 0,
location: "",
},
validationSchema: validationSchema,
onSubmit: async (values) => {
console.log(values);
// Caster les dates au format Day.js
values.manufacturingDate = dayjs(values.manufacturingDate).format('YYYY-MM-DD');
values.expiryDate = dayjs(values.expiryDate).format('YYYY-MM-DD');
// Vérifier les données
if (!values.productName || !values.description || !values.manufacturingDate || !values.expiryDate || !values.batchNumber || !values.quantity || !values.location) {
throw new Error("Les données sont incomplètes.");
}
try {
const response = await axios.post("http://localhost:5000/products/add",values, {
headers: {Authorization: `Bearer ${user.token}` }
});
console.log(response.status, response.data);
// Rediriger l'utilisateur vers la page de liste des produits
navigate('http://localhost:3000/products/list-products');
} catch (error) {
console.error(error);
}
// window.location.href = 'http://localhost:3000/products/list-products';
},
});
/************************************************************************************************************ */
return (
<>
<Grid >
<Paper elevation={20} style={paperStyle}>
<Grid container direction="column" justifyContent="center" alignItems="center">
<h2 style={headerStyle}>Ajouter un produit</h2>
<Typography variant='caption' gutterBottom>Veuillez remplir les détails ci-dessous</Typography>
</Grid>
<form onSubmit={formik.handleSubmit}>
<Grid container spacing={2} columns={16} >
{/* Left column */}
<Grid item xs={8}>
{/* nom du produit */}
<TextField
style={marginTop}
id='productName'
fullWidth
label='Nom du produit'
placeholder='Saisir le nom du produit'
InputProps={{
startAdornment: (
<InputAdornment position="start">
<FaGift />
</InputAdornment>
),
}}
value={formik.values.productName}
onChange={formik.handleChange}
error={formik.touched.productName && Boolean(formik.errors.productName)}
helperText={formik.touched.productName && formik.errors.productName}
/>
{/* description du produit */}
<TextField
style={marginTop}
id='description'
fullWidth
label='Decription'
placeholder='Saisir la description'
InputProps={{
startAdornment: (
<InputAdornment position="start">
<MdDescription />
</InputAdornment>
),
}}
value={formik.values.description}
onChange={formik.handleChange}
error={formik.touched.description && Boolean(formik.errors.description)}
helperText={formik.touched.description && formik.errors.description}
/>
{/* date de fabrication + date d'expiration */}
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DemoContainer components={['DatePicker', 'DatePicker']} >
{/* date de fabrication */}
<DatePicker
required
style={marginTop}
label="Date de fabrication"
value={dayjs(formik.values.manufacturingDate)}
onChange={(value) => {formik.setFieldValue('manufacturingDate', dayjs(value));}}
slotProps={{
textField: {
variant: 'outlined',
error: formik.touched.manufacturingDate && Boolean(formik.errors.manufacturingDate),
helperText: formik.touched.manufacturingDate && formik.errors.manufacturingDate,
},
}}
/>
{/* date d'expiration */}
<DatePicker
required
disablePast
minDate={dayjs()}
style={marginTop}
label="Date d'expiration"
value={dayjs(formik.values.expiryDate)}
onChange={(value) => {formik.setFieldValue('expiryDate', dayjs(value));}}
slotProps={{
textField: {
variant: 'outlined',
error: formik.touched.expiryDate && Boolean(formik.errors.expiryDate),
helperText: formik.touched.expiryDate && formik.errors.expiryDate,
},
}}
/>
</DemoContainer>
</LocalizationProvider>
</Grid>
{/* Right column */}
<Grid item xs={8}>
{/* Numéro de lot */}
<TextField
style={marginTop}
id='batchNumber'
fullWidth
label='Numéro de lot'
placeholder='Saisir le numéro de lot'
InputProps={{
startAdornment: (
<InputAdornment position="start">
<RiBarcodeBoxLine />
</InputAdornment>
),
}}
value={formik.values.batchNumber}
onChange={formik.handleChange}
error={formik.touched.batchNumber && Boolean(formik.errors.batchNumber)}
helperText={formik.touched.batchNumber && formik.errors.batchNumber}
/>
{/* Quantité */}
<TextField
style={marginTop}
id='quantity'
fullWidth
type='number'
inputProps={{min:0}}
label='Quantité'
placeholder='Saisir la quantité'
InputProps={{
startAdornment: (
<InputAdornment position="start">
<FaCartPlus />
</InputAdornment>
),
}}
value={formik.values.quantity}
onChange={formik.handleChange}
error={formik.touched.quantity && Boolean(formik.errors.quantity)}
helperText={formik.touched.quantity && formik.errors.quantity}
/>
{/* Emplacement */}
<TextField
style={marginTop}
id='location'
fullWidth
label='Emplacement'
placeholder= "Saisir l'emplacement"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<MdAddLocation />
</InputAdornment>
),
}}
value={formik.values.location}
onChange={formik.handleChange}
error={formik.touched.location && Boolean(formik.errors.location)}
helperText={formik.touched.location && formik.errors.location}
/>
</Grid>
{/* Bottom center */}
<Grid item xs={16}>
<Grid container justifyContent="center">
<Button type='submit' variant='contained' color='primary' style={marginTop}>
Ajouter
</Button>
</Grid>
</Grid>
</Grid>
</form>
</Paper>
</Grid>
</>
)
}
export default NewProduct;
My server Code Works and here is my createProduct function:
const createProduct = async (req, res) => {
const {name, description, manufacturingDate, expiryDate,
batchNumber, quantity, location} = req.body;
const requiredFields = ['name', 'description', 'manufacturingDate', 'expiryDate',
'batchNumber', 'quantity', 'location'];
const emptyFields = requiredFields.filter((field) => !req.body[field]);
if (emptyFields.length > 0) {
return res.status(400).json({error: 'Veuillez remplir tous les champs', emptyFields});
}
try {
const product = await Product.create({
name,
description,
manufacturingDate: dayjs(manufacturingDate,'YYYY-MM-DD').toDate(),
expiryDate: dayjs(expiryDate,'YYYY-MM-DD').toDate(),
batchNumber,
quantity,
location
});
res.status(201).json(product);
} catch (error) {
res.status(500).json({error: error.message});
}
}
I am trying to understand what is wrong. Thank you very much for your help !