Profile Image Added and Edit Api integration in profiePage , Vehicle Image bug fixed

This commit is contained in:
jaanvi 2025-05-01 17:30:15 +05:30
parent 570faa40d4
commit bf8799c396
8 changed files with 195 additions and 134 deletions

View file

@ -13,7 +13,7 @@ interface EditVehicleModalProps {
company: string,
modelName: string,
chargeType: string,
imageUrl: File | null
image: File | null
) => void;
editRow: any;
}
@ -23,7 +23,7 @@ interface FormData {
company: string;
modelName: string;
chargeType: string;
imageUrl: File | null;
image: File | null;
}
const EditVehicleModal: React.FC<EditVehicleModalProps> = ({
@ -44,49 +44,84 @@ const EditVehicleModal: React.FC<EditVehicleModalProps> = ({
company: "",
modelName: "",
chargeType: "",
imageUrl: null,
image: null,
},
});
const [imagePreview, setImagePreview] = useState<string | null>(null);
// Set form values and image preview when editRow changes
useEffect(() => {
if (editRow) {
// Set form fields
setValue("name", editRow.name || "");
setValue("company", editRow.company || "");
setValue("modelName", editRow.modelName || "");
setValue("chargeType", editRow.chargeType || "");
const IMAGE_BASE_URL = `${process.env.REACT_APP_BACKEND_URL}`;
useEffect(() => {
if (open && editRow) {
// Reset form with editRow values
reset({
name: editRow.name || "",
company: editRow.company || "",
modelName: editRow.modelName || "",
chargeType: editRow.chargeType || "",
image: editRow.image, // Always start with null for the form's image field
});
// Set image preview for existing image
if (editRow?.imageUrl) {
const imageUrl =
editRow.imageUrl.startsWith("http") ||
editRow.imageUrl.startsWith("blob")
? editRow.imageUrl
: `${process.env.REACT_APP_BACKEND_URL}/image/${editRow.imageUrl}`;
setImagePreview(imageUrl);
} else {
setImagePreview(null);
}
// Set image preview for existing image
if (editRow.image) {
const image = editRow.image.startsWith("http")
? editRow.image
: `${process.env.REACT_APP_BACKEND_URL}/image/${editRow.image}`;
setImagePreview(image);
} else {
// Reset form and preview when no editRow
reset();
setImagePreview(null);
}
}, [editRow, setValue, reset]);
} else {
reset();
setImagePreview(null);
}
}, [open, editRow, reset]);
// useEffect(() => {
// if (editRow) {
// // Set form fields
// setValue("name", editRow.name || "");
// setValue("company", editRow.company || "");
// setValue("modelName", editRow.modelName || "");
// setValue("chargeType", editRow.chargeType || "");
// // Set image preview for existing image
// if (editRow?.image) {
// const image =
// editRow.image.startsWith("http") ||
// editRow.image.startsWith("blob")
// ? editRow.image
// : `${process.env.REACT_APP_BACKEND_URL}/image/${editRow.image}`;
// setImagePreview(image);
// } else {
// setImagePreview(null);
// }
// } else {
// // Reset form and preview when no editRow
// reset();
// setImagePreview(null);
// }
// }, [editRow, setValue, reset]);
// Handle image upload
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
setImagePreview(URL.createObjectURL(file)); // Show preview of new image
setValue("imageUrl", file); // Update form with new file
// const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// const file = e.target.files?.[0];
// if (file) {
// setImagePreview(URL.createObjectURL(file)); // Show preview of new image
// setValue("image", file); // Update form with new file
// }
// };
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
if (imagePreview?.startsWith("blob:")) {
URL.revokeObjectURL(imagePreview);
}
};
const image = URL.createObjectURL(file);
setImagePreview(image);
setValue("image", file);
}
};
// Handle form submission
const onSubmit = (data: FormData) => {
handleUpdate(
@ -95,7 +130,7 @@ const EditVehicleModal: React.FC<EditVehicleModalProps> = ({
data.company,
data.modelName,
data.chargeType,
data.imageUrl // Pass File | null to handleUpdate
data.image // Pass File | null to handleUpdate
);
handleClose();
@ -382,11 +417,7 @@ const EditVehicleModal: React.FC<EditVehicleModalProps> = ({
color="#000000"
fontSize={"16px"}
>
Preview (
{imagePreview.startsWith("blob")
? "New"
: "Existing"}
):
Preview
</Typography>
<img
src={imagePreview}

View file

@ -27,7 +27,16 @@ export default function Header() {
// Use a ref to make the full click area act as anchor
const menuAnchorRef = useRef<HTMLDivElement | null>(null);
const [menuOpen, setMenuOpen] = useState(false);
const IMAGE_BASE_URL = `${process.env.REACT_APP_BACKEND_URL}`;
const displayUser = React.useMemo(
() => ({
profilePhoto: user?.profilePhoto
? `${IMAGE_BASE_URL}/image/${user.profilePhoto}`
: "/avatar.png",
}),
[user]
);
const handleClick = () => {
setMenuOpen((prev) => !prev);
};
@ -102,7 +111,7 @@ export default function Header() {
}}
/>
</Box> */}
{/* Notification and Profile Section */}
<Stack
direction="row"
@ -130,7 +139,7 @@ export default function Header() {
{/* Avatar */}
<Avatar
alt="User Avatar"
src="/avatar.png"
src={displayUser?.profilePhoto}
sx={{ width: 36, height: 36 }}
/>

View file

@ -13,8 +13,8 @@ interface EditProfileModalProps {
handleUpdate: (
name: string,
phone: string,
bio?: string,
profilePhoto?: string | null
bio: string,
profilePhoto?: File | string | null
) => void;
editUser: any;
}
@ -22,11 +22,10 @@ interface EditProfileModalProps {
interface FormData {
name: string;
phone: string;
bio?: string;
profilePhoto?: string | null;
bio: string;
profilePhoto?: File | string | null;
}
const EditProfileModal: React.FC<EditProfileModalProps> = ({
open,
handleClose,
@ -42,34 +41,47 @@ const EditProfileModal: React.FC<EditProfileModalProps> = ({
} = useForm<FormData>();
const [imagePreview, setImagePreview] = useState<string | null>(null);
const IMAGE_BASE_URL = `${process.env.REACT_APP_BACKEND_URL}`;
useEffect(() => {
if (editUser) {
setValue("name", editUser.name || "");
setValue("phone", editUser.phone || "");
setValue("bio", editUser.bio || "");
setImagePreview(editUser.profilePhoto || null);
if (open && editUser) {
reset({
name: editUser.name || "",
phone: editUser.phone || "",
bio: editUser.bio || "",
profilePhoto: editUser.profilePhoto || null,
});
if (editUser.profilePhoto) {
setImagePreview(
`${IMAGE_BASE_URL}/image/${editUser.profilePhoto}`
);
} else {
setImagePreview(null);
}
}
}, [editUser, setValue]);
}, [open, editUser, reset, IMAGE_BASE_URL]);
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
if (imagePreview?.startsWith("blob:")) {
URL.revokeObjectURL(imagePreview);
}
const imageUrl = URL.createObjectURL(file);
setImagePreview(imageUrl);
setValue("profilePhoto", imageUrl);
setValue("profilePhoto", file);
}
};
const handleModalClose = () => {
handleClose();
setImagePreview(null);
reset(); // Reset form
};
const onSubmit = (data: FormData) => {
console.log("Form Data:-----", data);
handleUpdate(data.name, data.phone, data.bio, data.profilePhoto);
reset();
handleUpdate(data.name, data.phone, data.bio, data.profilePhoto);
handleModalClose();
};

View file

@ -146,14 +146,14 @@ export default function VehicleViewModal({ open, setViewModal, id }: Props) {
>
<strong>Image:</strong>
</Typography>
{selectedVehicle.imageUrl ? (
{selectedVehicle.image ? (
<img
src={
selectedVehicle.imageUrl.startsWith(
selectedVehicle.image.startsWith(
"http"
)
? selectedVehicle.imageUrl
: `${process.env.REACT_APP_BACKEND_URL}/image/${selectedVehicle.imageUrl}`
? selectedVehicle.image
: `${process.env.REACT_APP_BACKEND_URL}/image/${selectedVehicle.image}`
}
alt="Vehicle"
style={{

View file

@ -45,26 +45,29 @@ const ProfilePage = () => {
const handleUpdate = (
name: string,
phone: string,
bio?: string,
profilePhoto?: string | null
bio: string,
profilePhoto: File|string | null
) => {
console.log("Dispatching updateProfile...");
console.log("Dispatching updateProfile...",profilePhoto);
dispatch(updateProfile({ name, phone, bio, profilePhoto }));
};
const IMAGE_BASE_URL = `${process.env.REACT_APP_BACKEND_URL}`;
const displayUser = useMemo(
() => ({
name: user?.name || "N/A",
email: user?.email || "N/A",
phone: user?.phone || "N/A",
bio: user?.bio || "No bio available.",
userType: user?.userType || "N/A",
profilePhoto: user?.profilePhoto
? `${IMAGE_BASE_URL}/image/${user.profilePhoto}`
: "/avatar.png",
}),
[user]
);
// Memoizing the user data for optimization
const displayUser = useMemo(
() => ({
name: user?.name || "N/A",
email: user?.email || "N/A",
phone: user?.phone || "N/A",
bio: user?.bio || "No bio available.",
userType: user?.userType || "N/A",
profilePhoto: user?.profilePhoto || "/avatar.png", // Default image path
}),
[user]
);
// Show loading indicator if data is being fetched
if (loading) {

View file

@ -29,7 +29,6 @@ export default function VehicleList() {
dispatch(vehicleList());
}, [dispatch]);
console.log("Backend URL:", process.env.REACT_APP_BACKEND_URL);
const handleClickOpen = () => {
setRowData(null);
@ -48,7 +47,7 @@ export default function VehicleList() {
company: string;
modelName: string;
chargeType: string;
imageFile: File;
imageFile: File ;
}) => {
try {
setIsAdding(true);
@ -70,7 +69,7 @@ const handleUpdate = (
company: string,
modelName: string,
chargeType: string,
imageUrl: File | null
image: File | null
) => {
dispatch(
updateVehicle({
@ -79,7 +78,7 @@ const handleUpdate = (
company,
modelName,
chargeType,
imageUrl, // File or null
image, // File or null
})
);
};
@ -87,7 +86,7 @@ const handleUpdate = (
const categoryColumns: Column[] = [
{ id: "srno", label: "Sr No" },
{ id: "imageUrl", label: "Image" },
{ id: "image", label: "Image" },
{ id: "name", label: "Vehicle Name" },
{ id: "company", label: "Company" },
{ id: "modelName", label: "Model Name" },
@ -96,40 +95,37 @@ const handleUpdate = (
{ id: "action", label: "Action", align: "center" },
];
const categoryRows = vehicles?.length
? vehicles?.map(
(
vehicle: {
id: number;
name: string;
company: string;
modelName: string;
chargeType: string;
imageUrl: string;
},
index: number
) => {
const imageUrl = vehicle?.imageUrl
? `${process.env.REACT_APP_BACKEND_URL}/image/${vehicle?.imageUrl}`
: "/images/fallback.jpg";
console.log(
"Vehicle:",
vehicle.name,
"Image URL:",
imageUrl
);
return {
id: vehicle?.id,
srno: index + 1,
name: vehicle?.name,
company: vehicle?.company,
modelName: vehicle?.modelName,
chargeType: vehicle?.chargeType,
imageUrl,
};
}
)
: [];
const categoryRows = vehicles?.length
? vehicles.map(
(
vehicle: {
id: number;
name: string;
company: string;
modelName: string;
chargeType: string;
image: string ;
},
index: number
) => {
const image = vehicle?.image
? typeof vehicle.image === "string" &&
vehicle.image.startsWith("http")
? vehicle.image
: `${process.env.REACT_APP_BACKEND_URL}/image/${vehicle.image}`
: "/images/fallback.jpg"; // Fallback image
return {
id: vehicle.id,
srno: index + 1,
name: vehicle.name,
company: vehicle.company,
modelName: vehicle.modelName,
chargeType: vehicle.chargeType,
image,
};
}
)
: [];
return (
<>

View file

@ -15,7 +15,7 @@ interface Vehicle {
company: string;
modelName: string;
chargeType: string;
imageUrl: string;
image: string;
}
interface VehicleState {
@ -120,13 +120,13 @@ export const updateVehicle = createAsyncThunk<
company: string;
modelName: string;
chargeType: string;
imageUrl: File | string | null;
image: File | string | null;
},
{ rejectValue: string }
>(
"updateVehicle",
async (
{ id, name, company, modelName, chargeType, imageUrl },
{ id, name, company, modelName, chargeType, image },
{ rejectWithValue }
) => {
try {
@ -136,8 +136,8 @@ export const updateVehicle = createAsyncThunk<
formData.append("modelName", modelName);
formData.append("chargeType", chargeType);
if (imageUrl instanceof File) {
formData.append("image", imageUrl); // Append new file
if (image instanceof File) {
formData.append("image", image); // Append new file
}
const response = await http.patch(

View file

@ -10,7 +10,7 @@ interface User {
email: string;
userType: string;
phone: string;
bio?: string;
bio: string;
profilePhoto?: string;
}
@ -53,29 +53,40 @@ export const fetchAdminProfile = createAsyncThunk<
});
export const updateProfile = createAsyncThunk<
User,
User,
{
name: string;
phone?: string;
bio?: string;
profilePhoto?: string | null;
phone: string;
bio: string;
profilePhoto?: File | string | null;
},
{ rejectValue: string }
>(
"updateProfile",
async ({ name, phone, bio, profilePhoto }, { rejectWithValue }) => {
try {
const payload: any = { name };
if (phone) payload.phone = phone;
if (bio) payload.bio = bio;
if (profilePhoto) payload.profilePhoto = profilePhoto;
const response = await http.put("/edit-profile", payload);
console.log("-----------", response);
// Create form data
const formData = new FormData();
formData.append("name", name);
formData.append("phone", phone);
formData.append("bio", bio);
if (profilePhoto instanceof File) {
formData.append("profilePhoto", profilePhoto); // ✅ File
}
// Make the request with the form data
const response = await http.put("/edit-profile", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
// Handle success
toast.success("Profile updated successfully");
return response.data.data;
} catch (error: any) {
// Handle error
toast.error("Error updating the profile: " + error.message);
return rejectWithValue(
error?.response?.data?.message || "An error occurred"
@ -84,7 +95,6 @@ export const updateProfile = createAsyncThunk<
}
);
const profileSlice = createSlice({
name: "profile",
initialState,