dev-jaanvi #1

Open
jaanvi wants to merge 155 commits from dev-jaanvi into main
8 changed files with 195 additions and 134 deletions
Showing only changes of commit bf8799c396 - Show all commits

View file

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

View file

@ -27,7 +27,16 @@ export default function Header() {
// Use a ref to make the full click area act as anchor // Use a ref to make the full click area act as anchor
const menuAnchorRef = useRef<HTMLDivElement | null>(null); const menuAnchorRef = useRef<HTMLDivElement | null>(null);
const [menuOpen, setMenuOpen] = useState(false); 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 = () => { const handleClick = () => {
setMenuOpen((prev) => !prev); setMenuOpen((prev) => !prev);
}; };
@ -130,7 +139,7 @@ export default function Header() {
{/* Avatar */} {/* Avatar */}
<Avatar <Avatar
alt="User Avatar" alt="User Avatar"
src="/avatar.png" src={displayUser?.profilePhoto}
sx={{ width: 36, height: 36 }} sx={{ width: 36, height: 36 }}
/> />

View file

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

View file

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

View file

@ -45,26 +45,29 @@ const ProfilePage = () => {
const handleUpdate = ( const handleUpdate = (
name: string, name: string,
phone: string, phone: string,
bio?: string, bio: string,
profilePhoto?: string | null profilePhoto: File|string | null
) => { ) => {
console.log("Dispatching updateProfile..."); console.log("Dispatching updateProfile...",profilePhoto);
dispatch(updateProfile({ name, phone, bio, 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 // Show loading indicator if data is being fetched
if (loading) { if (loading) {

View file

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

View file

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

View file

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