I am working on a project which takes image files and other data on the mongo db. While registring user i am taking a profile picture and it is uploading correctly. And once the user register by selecting role as caregiver, then i am giving them the form for applying for caregiver account in which i am taking certificates(array) images and they also are uploading without any issue. But the issue is when i try to update the profile with new profile picture and certificates then I am unable to recieve data on the backend. And it works fine with other routes but with this route I am getting req.body as empty and getting this on console : http://localhost:8070/undefined 404
This is my modal for updating profile:
import {
Avatar,
Box,
Button,
Chip,
Modal,
Paper,
TextField,
Typography,
} from "@mui/material";
import { useEffect, useState } from "react";
import { AiOutlineCloudUpload } from "react-icons/ai";
import { useDispatch, useSelector } from "react-redux";
import { hideLoading, showLoading } from "../../redux/features/alertSlice";
import axios from "axios";
import { toast } from "react-toastify";
import { useParams } from "react-router-dom";
const UpdateProfileModal = ({ isOpen, onClose, caregiver }) => {
const [editedData, setEditedData] = useState(null);
const { user } = useSelector((state) => state.user);
const dispatch = useDispatch();
const params = useParams();
const [profilePictureDisplay, setProfilePictureDisplay] = useState(null);
useEffect(() => {
const getNurseInfo = async () => {
try {
const res = await axios.post(
"http://localhost:8070/api/v1/caregiver/getCaregiverInfo",
{ userId: params.id },
{
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
}
);
if (res.data.success) {
setEditedData(res.data.data);
}
} catch (error) {
console.log(error);
}
};
getNurseInfo();
}, [params.id]);
const handleInputChange = (e) => {
const { name, value } = e.target;
// Special handling for preferredCities to convert the string to an array
if (name === "preferredCities") {
const citiesArray = value.split(",").map((city) => city.trim());
setEditedData((prevData) => ({
...prevData,
[name]: citiesArray,
}));
} else if (name === "qualification") {
const qualArray = value.split(",").map((qual) => qual.trim());
setEditedData((prevData) => ({
...prevData,
[name]: qualArray,
}));
} else if (name === "specialisation") {
const specArray = value.split(",").map((spec) => spec.trim());
setEditedData((prevData) => ({
...prevData,
[name]: specArray,
}));
} else {
// For other fields, directly update the state
setEditedData((prevData) => ({
...prevData,
[name]: value,
}));
}
};
const handleFileChange = (event) => {
const file = event.target.files[0];
if (file) {
setProfilePictureDisplay(URL.createObjectURL(file));
setEditedData((prevData) => ({
...prevData,
profilePicture: file,
}));
}
};
const handleCertificateImageChange = (e) => {
const files = e.target.files;
const fileList = Array.from(files);
const imageFiles = fileList.filter((file) =>
file.type.startsWith("image/")
);
setEditedData((prevData) => ({
...prevData,
certifications: [...prevData.certifications, ...imageFiles],
}));
};
const removeCertificate = (certificate) => {
setEditedData((prevData) => ({
...prevData,
certifications: prevData.certifications.filter(
(cert) => cert !== certificate
),
}));
};
const handleSumbit = async (e) => {
e.preventDefault();
const formdata = new FormData();
formdata.append("userId", user._id);
formdata.append("name", editedData.name);
formdata.append("address", editedData.address);
formdata.append("yearsExperience", editedData.yearsExperience);
formdata.append("feesPerDay", editedData.feesPerDay);
formdata.append("description", editedData.description);
formdata.append("ageRange", editedData.ageRange);
formdata.append("availability", editedData.availability);
formdata.append(
"preferredCities",
JSON.stringify(editedData.preferredCities)
);
formdata.append("qualification", JSON.stringify(editedData.qualification));
formdata.append(
"specialisation",
JSON.stringify(editedData.specialisation)
);
formdata.append("profilePicture", editedData.profilePicture);
editedData.certifications.forEach((certification, index) => {
formdata.append(`certifications[${index}]`, certification);
});
try {
dispatch(showLoading());
const res = await axios.patch(
"http://localhost:8070/api/v1/caregiver/updateCaregiver",
formdata,
{
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
"Content-Type": "multipart/form-data",
},
}
);
dispatch(hideLoading());
if (res.data.success) {
toast.success(res.data.message, {
position: toast.POSITION.TOP_CENTER,
});
onClose();
} else {
toast.error(res.data.message, {
position: toast.POSITION.TOP_CENTER,
});
}
} catch (error) {
dispatch(hideLoading());
console.log(error);
toast.error("Something went wrong", {
position: toast.POSITION.TOP_CENTER,
});
}
};
return (
<>
<div className="">
<Modal
open={isOpen}
onClose={onClose}
className="flex items-center justify-center pt-44 overflow-y-scroll"
>
<div className="">
<form
action=""
method="patch"
encType="multipart/form-data"
className="max-w-4xl"
onSubmit={handleSumbit}
>
<Box className="mt-14">
<Paper elevation={3} className="p-6">
<div className="bg-white p-4">
{/* Profile Picture and Avatar */}
<div className="flex items-center space-x-4">
<div className="bg-white p-3 rounded shadow-md w-full">
<div className="mt-1 bg-[#f3f4f6] flex items-center justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md">
<div className="space-y-1 text-center">
<label
htmlFor="file-upload"
className="relative cursor-pointer rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:border-indigo-300"
>
<span className="flex items-center justify-center gap-1">
<AiOutlineCloudUpload /> Upload a profile
picture
</span>
<input
id="file-upload"
name="profilePicture"
type="file"
accept="image/png,image/jpg,image/jpeg"
className="sr-only"
onChange={handleFileChange}
/>
</label>
<p className="text-xs text-gray-500">
PNG, JPG, JPEG up to 10MB
</p>
</div>
</div>
</div>
<Box>
<Avatar
alt="Profile Picture"
src={`http://localhost:8070/${editedData?.profilePicture}`}
sx={{ width: 100, height: 100 }}
/>
</Box>
</div>
<div className=" flex items-start justify-start gap-3">
{/* Name */}
<TextField
label="Name"
name="name"
value={editedData?.name}
onChange={handleInputChange}
fullWidth
className="mb-2"
/>
{/* Address */}
<TextField
label="Address"
name="address"
value={editedData?.address}
onChange={handleInputChange}
fullWidth
className="mb-2"
/>
</div>
<TextField
label="Years of experience"
name="yearsExperience"
value={editedData?.yearsExperience}
onChange={handleInputChange}
fullWidth
className="mb-2"
/>
<div className=" flex items-start justify-start gap-3">
{/* Age Range */}
<TextField
label="Age Range Lower Limit"
value={editedData?.ageRange?.lowerLimit}
name="ageRange"
onChange={handleInputChange}
fullWidth
className="mb-2"
/>
<TextField
label="Age Range Upper Limit"
value={editedData?.ageRange?.upperLimit}
name="ageRange"
onChange={handleInputChange}
fullWidth
className="mb-2"
/>
</div>
{/* Availability */}
<TextField
label="Availability"
name="availability"
value={editedData?.availability}
onChange={handleInputChange}
fullWidth
className="mb-2"
/>
{/* Certifications */}
<div className="flex items-start justify-center flex-col space-y-2 ">
<div className="bg-white p-3 rounded shadow-md w-full">
<div className="mt-1 bg-[#f3f4f6] flex items-center justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md">
<div className="space-y-1 text-center ">
<label
htmlFor="file-upload"
className="relative cursor-pointer rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:border-indigo-300"
>
<span className="flex items-center justify-center gap-1">
<AiOutlineCloudUpload />
Upload a file (certificates if any)
</span>
<input
id="file-upload"
name="certifications"
multiple
type="file"
accept="image/png,image/jpg,image/jpeg"
className="sr-only"
onChange={handleCertificateImageChange}
/>
</label>
<p className="text-xs text-gray-500">
PNG, JPG, JPEG up to 10MB
</p>
</div>
</div>
</div>
<div className="h-4">
<span className="text-red-500 text-sm mt-1"></span>
</div>
<div className="flex items-center justify-start gap-2 w-full flex-wrap">
{editedData?.certifications &&
editedData?.certifications?.map((img, index) => {
return (
<>
<div>
<Chip
key={index}
label={img}
onDelete={() => removeCertificate(img)}
/>
<a
href={`http://localhost:8070/${img}`}
rel="noreferrer"
target="_blank"
>
Veiw
</a>
</div>
</>
);
})}
</div>
</div>
{/* <TextField
label="Certifications"
name="certifications"
value={editedData.certifications.join(", ")}
onChange={handleInputChange}
fullWidth
className="mb-2"
/> */}
{/* <div className=" flex items-start justify-start gap-3"> */}
{/* City */}
<TextField
label="City"
name="city"
value={editedData?.city}
onChange={handleInputChange}
fullWidth
className="mb-2"
/>
{/* Preferred Cities */}
<div className="mb-2">
<TextField
label="Preferred cities"
name="preferredCities"
value={editedData?.preferredCities.join(", ")}
onChange={handleInputChange}
fullWidth
className="mb-2"
/>
<TextField
label="Qualification"
name="qualification"
value={editedData?.qualification.join(", ")}
onChange={handleInputChange}
fullWidth
className="mb-2"
/>
{/* <div>
{editedData.preferredCities.map((city, index) => (
<Chip key={index} label={city} className="mr-1 mb-1" />
))}
</div> */}
</div>
{/* </div> */}
<TextField
label="Specialisation"
name="specialisation"
value={editedData?.specialisation.join(", ")}
onChange={handleInputChange}
fullWidth
className="mb-2"
/>
<Button
variant="contained"
type="submit"
className="py-2 px-4 w-full bg-[#1976d2] hover:bg-[#1565c0]"
>
Save Changes
</Button>
</div>
</Paper>
</Box>
</form>
</div>
</Modal>
</div>
</>
);
};
export default UpdateProfileModal;
This is my server.js
const express = require("express");
const morgan = require("morgan");
const dotenv = require("dotenv");
const connnectDB = require("./config/db");
const cors = require('cors')
const path = require("path")
// dotenv config
dotenv.config();
//mongodb connection
connnectDB();
//rest object
const app = express();
//allow requests from frontend
app.use(cors({
origin: ['http://localhost:5173']
}))
// middlewares
app.use(express.json());
app.use(morgan("dev"));
// Example configuration
app.use(express.urlencoded({ extended: true }));
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));
// Handle file upload
// app.use('/api/v1/upload', upload.single('image'), require("./routes/uploadRoutes"))
// routes
app.use('/api/v1/user', require('./routes/userRoutes'))
app.use('/api/v1/admin', require('./routes/adminRoutes'))
app.use('/api/v1/caregiver', require('./routes/caregiverRoutes'))
//port
const port = process.env.PORT || 8070;
//listen port
app.listen(port, () => {
console.log(
`Server running in ${process.env.NODE_MODE} mode on port ${process.env.PORT}`
);
});
This is my route
router.patch('/updateCaregiver', upload.single('profilePicture'), upload.array('certifications', 10), authMiddleware, updateCaregiverController)
CaregiverController
const updateCaregiverController = async (req, res) => {
try {
const user = await userModel.findOne({ _id: req.body.userId })
const caregiver = await caregiverModel.findOne({ userId: req.body.userId })
// const ageRange = JSON.parse(req.body.ageRange)
let updatedCaregiver
let updatedUser
const {
name,
address,
yearsExperience,
feesPerDay,
preferredCities,
description,
qualification,
specialisation,
ageRange,
availability,
userId
} = req.body;
console.log("REQUEST BODY:", req.body);
if (caregiver) {
updatedCaregiver = await caregiverModel.findOneAndUpdate({ userId: userId }, {
yearsExperience,
feesPerDay,
preferredCities,
description,
qualification,
specialisation, ageRange, availability
}, {
runValidators: true, new: true
})
if (req.files && req.files.certifications) {
updatedCaregiver.certifications = req.files.certifications.map(file => file.path);
await updatedCaregiver.save();
}
}
if (user) {
updatedUser = await userModel.findOneAndUpdate(
{ _id: req.body.userId },
{
name,
address,
},
{ runValidators: true, new: true }
);
if (req.files && req.files.profilePicture && req.files.profilePicture.length > 0) {
updatedUser = await userModel.findOneAndUpdate(
{ _id: req.body.userId },
{
profilePicture: req.files.profilePicture[0].path
},
{ runValidators: true, new: true }
);
updatedUser.profilePicture = req.files.profilePicture[0].path;
await updatedUser.save();
}
}
const data = Object.assign(updatedCaregiver, updatedUser)
res.status(200).send({
success: true,
message: "Profile updated successfully",
data
});
} catch (error) {
console.log(error)
res.status(500).send({
success: false,
message: "Error while updating nurse details",
error
})
}
}
multerConfig.js
const multer = require('multer');
const path = require('path');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/');
},
filename: function (req, file, cb) {
cb(null, Date.now() + '-' + file.originalname);
},
});
const upload = multer({ storage: storage });
module.exports = upload;
Any help would be appreciated!
Thank you!