From 2f80a9a834677dc88b5351ddaf20444a088726aa Mon Sep 17 00:00:00 2001
From: Vishnu Sreekumaran Nair <200557136@student.georgianc.on.ca>
Date: Mon, 10 Feb 2025 16:35:47 -0500
Subject: [PATCH 01/28] create ImageUpload component and extract logic from
profilepanel
---
Client/src/Components/ImageUpload/index.jsx | 143 ++++++++++++++++++++
1 file changed, 143 insertions(+)
create mode 100644 Client/src/Components/ImageUpload/index.jsx
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
new file mode 100644
index 000000000..d8656e0b7
--- /dev/null
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -0,0 +1,143 @@
+import { useState, useRef } from "react";
+import { Box, Button, Stack, Typography } from "@mui/material";
+import ImageField from "../Inputs/Image";
+import ProgressUpload from "../ProgressBars";
+import { formatBytes } from "../../Utils/fileUtils";
+import { imageValidation } from "../../Validation/validation";
+import ImageIcon from "@mui/icons-material/Image";
+import GenericDialog from "../Dialog/genericDialog";
+
+/**
+ * ImageUpload component handles the uploading and updating of the profile picture.
+ *
+ * @param {Object} props - Component props.
+ * @param {boolean} props.open - Controls the visibility of the modal.
+ * @param {Function} props.onClose - Callback to close the modal.
+ * @param {Function} props.onUpdate - Callback to update the profile picture.
+ * @param {string} props.currentImage - The current profile image URL or base64 string.
+ * @returns {JSX.Element}
+ */
+const ImageUpload = ({ open, onClose, onUpdate, currentImage }) => {
+ const [file, setFile] = useState();
+ const [progress, setProgress] = useState({ value: 0, isLoading: false });
+ const [errors, setErrors] = useState({});
+ const intervalRef = useRef(null);
+
+ // Handles image file selection
+ const handlePicture = (event) => {
+ const pic = event.target.files[0];
+ let error = validateField({ type: pic.type, size: pic.size }, imageValidation);
+ if (error) return;
+
+ setProgress((prev) => ({ ...prev, isLoading: true }));
+ setFile({
+ src: URL.createObjectURL(pic),
+ name: pic.name,
+ size: formatBytes(pic.size),
+ delete: false,
+ });
+
+ // Simulate upload progress
+ intervalRef.current = setInterval(() => {
+ const buffer = 12;
+ setProgress((prev) => {
+ if (prev.value + buffer >= 100) {
+ clearInterval(intervalRef.current);
+ return { value: 100, isLoading: false };
+ }
+ return { ...prev, value: prev.value + buffer };
+ });
+ }, 120);
+ };
+
+ // Validates input against provided schema and updates error state
+ const validateField = (toValidate, schema, name = "picture") => {
+ const { error } = schema.validate(toValidate, { abortEarly: false });
+ setErrors((prev) => {
+ const prevErrors = { ...prev };
+ if (error) prevErrors[name] = error.details[0].message;
+ else delete prevErrors[name];
+ return prevErrors;
+ });
+ if (error) return true;
+ };
+
+ // Resets picture-related states and clears interval
+ const removePicture = () => {
+ errors["picture"] && setErrors((prev) => ({ ...prev, picture: undefined }));
+ setFile({ delete: true });
+ clearInterval(intervalRef.current);
+ setProgress({ value: 0, isLoading: false });
+ };
+
+ // Updates the profile picture and closes the modal
+ const handleUpdatePicture = () => {
+ setProgress({ value: 0, isLoading: false });
+ onUpdate(file.src); // Pass the new image URL to the parent component
+ onClose(); // Close the modal
+ };
+
+ return (
+
+
+ {progress.isLoading || progress.value !== 0 || errors["picture"] ? (
+ }
+ label={file?.name}
+ size={file?.size}
+ progress={progress.value}
+ onClick={removePicture}
+ error={errors["picture"]}
+ />
+ ) : (
+ ""
+ )}
+
+
+
+
+
+ );
+};
+
+export default ImageUpload;
\ No newline at end of file
From 2670375eb3050416045b41588c5bcbdb52f477de Mon Sep 17 00:00:00 2001
From: Vishnu Sreekumaran Nair <200557136@student.georgianc.on.ca>
Date: Mon, 10 Feb 2025 16:39:24 -0500
Subject: [PATCH 02/28] update profilepanel to use the imageupload component
---
.../TabPanels/Account/ProfilePanel.jsx | 207 +++---------------
1 file changed, 30 insertions(+), 177 deletions(-)
diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx
index e20cb13a4..707a6d035 100644
--- a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx
+++ b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx
@@ -1,30 +1,18 @@
import { useTheme } from "@emotion/react";
-import { useRef, useState } from "react";
+import { useState } from "react";
import TabPanel from "@mui/lab/TabPanel";
import { Box, Button, Divider, Stack, Typography } from "@mui/material";
import Avatar from "../../Avatar";
import TextInput from "../../Inputs/TextInput";
-import ImageField from "../../Inputs/Image";
-import { credentials, imageValidation } from "../../../Validation/validation";
+import { credentials } from "../../../Validation/validation";
import { useDispatch, useSelector } from "react-redux";
import { clearAuthState, deleteUser, update } from "../../../Features/Auth/authSlice";
-import ImageIcon from "@mui/icons-material/Image";
-import ProgressUpload from "../../ProgressBars";
-import { formatBytes } from "../../../Utils/fileUtils";
import { clearUptimeMonitorState } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
import { createToast } from "../../../Utils/toastUtils";
import { logger } from "../../../Utils/Logger";
import LoadingButton from "@mui/lab/LoadingButton";
import { GenericDialog } from "../../Dialog/genericDialog";
-import Dialog from "../../Dialog";
-
-/**
- * ProfilePanel component displays a form for editing user profile information
- * and allows for actions like updating profile picture, credentials,
- * and deleting account.
- *
- * @returns {JSX.Element}
- */
+import ImageUpload from "../../ImageUpload"; // Import the new ImageUpload component
const ProfilePanel = () => {
const theme = useTheme();
@@ -32,30 +20,25 @@ const ProfilePanel = () => {
const SPACING_GAP = theme.spacing(12);
- //redux state
+ // Redux state
const { user, authToken, isLoading } = useSelector((state) => state.auth);
const idToName = {
"edit-first-name": "firstName",
"edit-last-name": "lastName",
- // Disabled for now, will revisit in the future
- // "edit-email": "email",
};
- // Local state for form data, errors, and file handling
+ // Local state for form data and errors
const [localData, setLocalData] = useState({
firstName: user.firstName,
lastName: user.lastName,
- // email: user.email, // Disabled for now
});
const [errors, setErrors] = useState({});
- const [file, setFile] = useState();
- const intervalRef = useRef(null);
- const [progress, setProgress] = useState({ value: 0, isLoading: false });
+ const [isOpen, setIsOpen] = useState("");
// Handles input field changes and performs validation
const handleChange = (event) => {
- errors["unchanged"] && clearError("unchanged");
+ errors["unchanged"] && setErrors((prev) => ({ ...prev, unchanged: undefined }));
const { value, id } = event.target;
const name = idToName[id];
setLocalData((prev) => ({
@@ -66,33 +49,6 @@ const ProfilePanel = () => {
validateField({ [name]: value }, credentials, name);
};
- // Handles image file
- const handlePicture = (event) => {
- const pic = event.target.files[0];
- let error = validateField({ type: pic.type, size: pic.size }, imageValidation);
- if (error) return;
-
- setProgress((prev) => ({ ...prev, isLoading: true }));
- setFile({
- src: URL.createObjectURL(pic),
- name: pic.name,
- size: formatBytes(pic.size),
- delete: false,
- });
-
- //TODO - potentitally remove, will revisit in the future
- intervalRef.current = setInterval(() => {
- const buffer = 12;
- setProgress((prev) => {
- if (prev.value + buffer >= 100) {
- clearInterval(intervalRef.current);
- return { value: 100, isLoading: false };
- }
- return { ...prev, value: prev.value + buffer };
- });
- }, 120);
- };
-
// Validates input against provided schema and updates error state
const validateField = (toValidate, schema, name = "picture") => {
const { error } = schema.validate(toValidate, { abortEarly: false });
@@ -102,51 +58,6 @@ const ProfilePanel = () => {
else delete prevErrors[name];
return prevErrors;
});
- if (error) return true;
- };
-
- // Clears specific error from errors state
- const clearError = (err) => {
- setErrors((prev) => {
- const updatedErrors = { ...prev };
- if (updatedErrors[err]) delete updatedErrors[err];
- return updatedErrors;
- });
- };
-
- // Resets picture-related states and clears interval
- const removePicture = () => {
- errors["picture"] && clearError("picture");
- setFile({ delete: true });
- clearInterval(intervalRef.current); // interrupt interval if image upload is canceled prior to completing the process
- setProgress({ value: 0, isLoading: false });
- };
-
- // Opens the picture update modal
- const openPictureModal = () => {
- setIsOpen("picture");
- setFile({ delete: localData.deleteProfileImage });
- };
-
- // Closes the picture update modal and resets related states
- const closePictureModal = () => {
- errors["picture"] && clearError("picture");
- setFile(); //reset file
- clearInterval(intervalRef.current); // interrupt interval if image upload is canceled prior to completing the process
- setProgress({ value: 0, isLoading: false });
- setIsOpen("");
- };
-
- // Updates profile image displayed on UI
- const handleUpdatePicture = () => {
- setProgress({ value: 0, isLoading: false });
- setLocalData((prev) => ({
- ...prev,
- file: file.src,
- deleteProfileImage: false,
- }));
- setIsOpen("");
- errors["unchanged"] && clearError("unchanged");
};
// Handles form submission to update user profile
@@ -177,13 +88,21 @@ const ProfilePanel = () => {
}
};
- // Removes current profile image from UI
+ // Handles updating the profile picture
+ const handleUpdatePicture = (newImage) => {
+ setLocalData((prev) => ({
+ ...prev,
+ file: newImage,
+ deleteProfileImage: false,
+ }));
+ };
+
+ // Handles deleting the profile picture
const handleDeletePicture = () => {
setLocalData((prev) => ({
...prev,
deleteProfileImage: true,
}));
- errors["unchanged"] && clearError("unchanged");
};
// Initiates the account deletion process
@@ -194,12 +113,10 @@ const ProfilePanel = () => {
dispatch(clearUptimeMonitorState());
} else {
if (action.payload) {
- // dispatch errors
createToast({
body: action.payload.msg,
});
} else {
- // unknown errors
createToast({
body: "Unknown error.",
});
@@ -207,10 +124,6 @@ const ProfilePanel = () => {
}
};
- // Modal state and control functions
- const [isOpen, setIsOpen] = useState("");
- const isModalOpen = (name) => isOpen === name;
-
return (
{
direction="row"
gap={SPACING_GAP}
>
- {/* This 0.9 is a bit magic numbering, refactor */}
First name
@@ -317,16 +229,16 @@ const ProfilePanel = () => {
sx={{ marginRight: "8px" }}
/>
@@ -390,8 +302,8 @@ const ProfilePanel = () => {
)}
-
);
};
-ProfilePanel.propTypes = {
- // No props are being passed to this component, hence no specific PropTypes are defined.
-};
-
-export default ProfilePanel;
+export default ProfilePanel;
\ No newline at end of file
From edd98bfaa84d6751ebf66b4292d15a3f29310a46 Mon Sep 17 00:00:00 2001
From: Vishnu Sreekumaran Nair <200557136@student.georgianc.on.ca>
Date: Mon, 10 Feb 2025 16:46:34 -0500
Subject: [PATCH 03/28] pass theme prop to GenericDialog in ImageUpload
component
---
Client/src/Components/ImageUpload/index.jsx | 244 +++++++++---------
.../TabPanels/Account/ProfilePanel.jsx | 1 +
2 files changed, 120 insertions(+), 125 deletions(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index d8656e0b7..c509228c8 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -5,139 +5,133 @@ import ProgressUpload from "../ProgressBars";
import { formatBytes } from "../../Utils/fileUtils";
import { imageValidation } from "../../Validation/validation";
import ImageIcon from "@mui/icons-material/Image";
-import GenericDialog from "../Dialog/genericDialog";
+import {GenericDialog} from "../Dialog/genericDialog";
-/**
- * ImageUpload component handles the uploading and updating of the profile picture.
- *
- * @param {Object} props - Component props.
- * @param {boolean} props.open - Controls the visibility of the modal.
- * @param {Function} props.onClose - Callback to close the modal.
- * @param {Function} props.onUpdate - Callback to update the profile picture.
- * @param {string} props.currentImage - The current profile image URL or base64 string.
- * @returns {JSX.Element}
- */
-const ImageUpload = ({ open, onClose, onUpdate, currentImage }) => {
- const [file, setFile] = useState();
- const [progress, setProgress] = useState({ value: 0, isLoading: false });
- const [errors, setErrors] = useState({});
- const intervalRef = useRef(null);
+const ImageUpload = ({ open, onClose, onUpdate, currentImage, theme }) => {
+ const [file, setFile] = useState();
+ const [progress, setProgress] = useState({ value: 0, isLoading: false });
+ const [errors, setErrors] = useState({});
+ const intervalRef = useRef(null);
- // Handles image file selection
- const handlePicture = (event) => {
- const pic = event.target.files[0];
- let error = validateField({ type: pic.type, size: pic.size }, imageValidation);
- if (error) return;
+ // Handles image file selection
+ const handlePicture = (event) => {
+ const pic = event.target.files[0];
+ let error = validateField({ type: pic.type, size: pic.size }, imageValidation);
+ if (error) return;
- setProgress((prev) => ({ ...prev, isLoading: true }));
- setFile({
- src: URL.createObjectURL(pic),
- name: pic.name,
- size: formatBytes(pic.size),
- delete: false,
- });
+ setProgress((prev) => ({ ...prev, isLoading: true }));
+ setFile({
+ src: URL.createObjectURL(pic),
+ name: pic.name,
+ size: formatBytes(pic.size),
+ delete: false,
+ });
- // Simulate upload progress
- intervalRef.current = setInterval(() => {
- const buffer = 12;
- setProgress((prev) => {
- if (prev.value + buffer >= 100) {
- clearInterval(intervalRef.current);
- return { value: 100, isLoading: false };
- }
- return { ...prev, value: prev.value + buffer };
- });
- }, 120);
- };
+ // Simulate upload progress
+ intervalRef.current = setInterval(() => {
+ const buffer = 12;
+ setProgress((prev) => {
+ if (prev.value + buffer >= 100) {
+ clearInterval(intervalRef.current);
+ return { value: 100, isLoading: false };
+ }
+ return { ...prev, value: prev.value + buffer };
+ });
+ }, 120);
+ };
- // Validates input against provided schema and updates error state
- const validateField = (toValidate, schema, name = "picture") => {
- const { error } = schema.validate(toValidate, { abortEarly: false });
- setErrors((prev) => {
- const prevErrors = { ...prev };
- if (error) prevErrors[name] = error.details[0].message;
- else delete prevErrors[name];
- return prevErrors;
- });
- if (error) return true;
- };
+ // Validates input against provided schema and updates error state
+ const validateField = (toValidate, schema, name = "picture") => {
+ const { error } = schema.validate(toValidate, { abortEarly: false });
+ setErrors((prev) => {
+ const prevErrors = { ...prev };
+ if (error) prevErrors[name] = error.details[0].message;
+ else delete prevErrors[name];
+ return prevErrors;
+ });
+ if (error) return true;
+ };
- // Resets picture-related states and clears interval
- const removePicture = () => {
- errors["picture"] && setErrors((prev) => ({ ...prev, picture: undefined }));
- setFile({ delete: true });
- clearInterval(intervalRef.current);
- setProgress({ value: 0, isLoading: false });
- };
+ // Resets picture-related states and clears interval
+ const removePicture = () => {
+ errors["picture"] && setErrors((prev) => ({ ...prev, picture: undefined }));
+ setFile({ delete: true });
+ clearInterval(intervalRef.current);
+ setProgress({ value: 0, isLoading: false });
+ };
- // Updates the profile picture and closes the modal
- const handleUpdatePicture = () => {
- setProgress({ value: 0, isLoading: false });
- onUpdate(file.src); // Pass the new image URL to the parent component
- onClose(); // Close the modal
- };
+ // Updates the profile picture and closes the modal
+ const handleUpdatePicture = () => {
+ setProgress({ value: 0, isLoading: false });
+ onUpdate(file.src); // Pass the new image URL to the parent component
+ onClose(); // Close the modal
+ };
- return (
-
-
- {progress.isLoading || progress.value !== 0 || errors["picture"] ? (
- }
- label={file?.name}
- size={file?.size}
- progress={progress.value}
- onClick={removePicture}
- error={errors["picture"]}
- />
- ) : (
- ""
- )}
-
-
-
-
-
- );
+ return (
+
+
+ {progress.isLoading || progress.value !== 0 || errors["picture"] ? (
+ }
+ label={file?.name}
+ size={file?.size}
+ progress={progress.value}
+ onClick={removePicture}
+ error={errors["picture"]}
+ />
+ ) : (
+ ""
+ )}
+
+
+
+
+
+ );
};
export default ImageUpload;
\ No newline at end of file
diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx
index 707a6d035..090bf7289 100644
--- a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx
+++ b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx
@@ -321,6 +321,7 @@ const ProfilePanel = () => {
onClose={() => setIsOpen("")}
onUpdate={handleUpdatePicture}
currentImage={user?.avatarImage ? `data:image/png;base64,${user.avatarImage}` : ""}
+ theme={theme}
/>
);
From 223cad00b25c50518ebadc192f762a07b96f96db Mon Sep 17 00:00:00 2001
From: Vishnu Sreekumaran Nair <200557136@student.georgianc.on.ca>
Date: Mon, 10 Feb 2025 16:58:49 -0500
Subject: [PATCH 04/28] resolve prop validation errors in GenericDialog and
ImageField
---
Client/src/Components/ImageUpload/index.jsx | 9 +++++----
Client/src/Components/TabPanels/Account/ProfilePanel.jsx | 5 ++++-
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index c509228c8..3fd86cb06 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -79,18 +79,19 @@ const ImageUpload = ({ open, onClose, onUpdate, currentImage, theme }) => {
isLoading={false}
>
+ />
{progress.isLoading || progress.value !== 0 || errors["picture"] ? (
}
diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx
index 090bf7289..38c26fb28 100644
--- a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx
+++ b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx
@@ -309,11 +309,14 @@ const ProfilePanel = () => {
description={
"If you delete your account, you will no longer be able to sign in, and all of your data will be deleted. Deleting your account is permanent and non-recoverable action."
}
+ onClose={() => setIsOpen("")}
onCancel={() => setIsOpen("")}
confirmationButtonLabel={"Delete account"}
onConfirm={handleDeleteAccount}
isLoading={isLoading}
- />
+ >
+ <>>
+
{/* Image Upload Modal */}
Date: Mon, 10 Feb 2025 18:30:45 -0500
Subject: [PATCH 05/28] add logic to handle base64 images and placeholders
---
Client/src/Components/ImageUpload/index.jsx | 39 +++++++++++++++++++--
1 file changed, 37 insertions(+), 2 deletions(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index 3fd86cb06..e235fb528 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -1,5 +1,5 @@
import { useState, useRef } from "react";
-import { Box, Button, Stack, Typography } from "@mui/material";
+import { Button, Stack } from "@mui/material";
import ImageField from "../Inputs/Image";
import ProgressUpload from "../ProgressBars";
import { formatBytes } from "../../Utils/fileUtils";
@@ -7,12 +7,47 @@ import { imageValidation } from "../../Validation/validation";
import ImageIcon from "@mui/icons-material/Image";
import {GenericDialog} from "../Dialog/genericDialog";
-const ImageUpload = ({ open, onClose, onUpdate, currentImage, theme }) => {
+const isValidBase64Image = (data) => {
+ return /^[A-Za-z0-9+/=]+$/.test(data);
+ };
+
+const ImageUpload = ({
+ open,
+ onClose,
+ onUpdate,
+ currentImage,
+ theme,
+ shouldRender = true,
+ alt = "Uploaded Image",
+ width = "auto",
+ height = "auto",
+ minWidth = "auto",
+ minHeight = "auto",
+ maxWidth = "auto",
+ maxHeight = "auto",
+ placeholder,
+ sx
+ }) => {
const [file, setFile] = useState();
const [progress, setProgress] = useState({ value: 0, isLoading: false });
const [errors, setErrors] = useState({});
const intervalRef = useRef(null);
+ // Handle base64 and placeholder logic
+ let imageSrc = currentImage;
+
+ if (typeof file?.src !== "undefined") {
+ imageSrc = file.src;
+ } else if (typeof currentImage !== "undefined" && isValidBase64Image(currentImage)) {
+ imageSrc = `data:image/png;base64,${currentImage}`;
+ } else if (typeof placeholder !== "undefined") {
+ imageSrc = placeholder;
+ }
+
+ if (shouldRender === false) {
+ return null;
+ }
+
// Handles image file selection
const handlePicture = (event) => {
const pic = event.target.files[0];
From e9afb903e9770705900bbba412ef12aad3b11ab3 Mon Sep 17 00:00:00 2001
From: Vishnu Sreekumaran Nair <200557136@student.georgianc.on.ca>
Date: Mon, 10 Feb 2025 18:34:48 -0500
Subject: [PATCH 06/28] update the rendering logic to use the new props
---
Client/src/Components/ImageUpload/index.jsx | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index e235fb528..bf4f8aa0e 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -1,5 +1,5 @@
import { useState, useRef } from "react";
-import { Button, Stack } from "@mui/material";
+import { Box, Button, Stack } from "@mui/material";
import ImageField from "../Inputs/Image";
import ProgressUpload from "../ProgressBars";
import { formatBytes } from "../../Utils/fileUtils";
@@ -113,6 +113,18 @@ const ImageUpload = ({
onConfirm={handleUpdatePicture}
isLoading={false}
>
+
Date: Mon, 10 Feb 2025 18:39:00 -0500
Subject: [PATCH 07/28] replace and comment image component usage for testing
---
Client/src/Components/Image/index.jsx | 128 +++++++++---------
Client/src/Components/StatBox/index.jsx | 5 +-
.../DistributedUptime/CreateStatus/index.jsx | 5 +-
.../Components/ControlsHeader/index.jsx | 5 +-
4 files changed, 73 insertions(+), 70 deletions(-)
diff --git a/Client/src/Components/Image/index.jsx b/Client/src/Components/Image/index.jsx
index 8f30f8a9e..92707de66 100644
--- a/Client/src/Components/Image/index.jsx
+++ b/Client/src/Components/Image/index.jsx
@@ -1,72 +1,72 @@
-import { Box } from "@mui/material";
-import PropTypes from "prop-types";
+// import { Box } from "@mui/material";
+// import PropTypes from "prop-types";
-const isValidBase64Image = (data) => {
- return /^[A-Za-z0-9+/=]+$/.test(data);
-};
+// const isValidBase64Image = (data) => {
+// return /^[A-Za-z0-9+/=]+$/.test(data);
+// };
-const Image = ({
- shouldRender = true,
- src,
- alt,
- width = "auto",
- height = "auto",
- minWidth = "auto",
- minHeight = "auto",
- maxWidth = "auto",
- maxHeight = "auto",
- base64,
- placeholder,
- sx,
-}) => {
- if (shouldRender === false) {
- return null;
- }
+// const Image = ({
+// shouldRender = true,
+// src,
+// alt,
+// width = "auto",
+// height = "auto",
+// minWidth = "auto",
+// minHeight = "auto",
+// maxWidth = "auto",
+// maxHeight = "auto",
+// base64,
+// placeholder,
+// sx,
+// }) => {
+// if (shouldRender === false) {
+// return null;
+// }
- if (typeof src !== "undefined" && typeof base64 !== "undefined") {
- console.warn("base64 takes precedence over src and overwrites it");
- }
+// if (typeof src !== "undefined" && typeof base64 !== "undefined") {
+// console.warn("base64 takes precedence over src and overwrites it");
+// }
- if (typeof base64 !== "undefined" && isValidBase64Image(base64)) {
- src = `data:image/png;base64,${base64}`;
- }
+// if (typeof base64 !== "undefined" && isValidBase64Image(base64)) {
+// src = `data:image/png;base64,${base64}`;
+// }
- if (
- typeof src === "undefined" &&
- typeof base64 === "undefined" &&
- typeof placeholder !== "undefined"
- ) {
- src = placeholder;
- }
+// if (
+// typeof src === "undefined" &&
+// typeof base64 === "undefined" &&
+// typeof placeholder !== "undefined"
+// ) {
+// src = placeholder;
+// }
- return (
-
- );
-};
+// return (
+//
+// );
+// };
-Image.propTypes = {
- shouldRender: PropTypes.bool,
- src: PropTypes.string,
- alt: PropTypes.string.isRequired,
- width: PropTypes.string,
- height: PropTypes.string,
- minWidth: PropTypes.string,
- minHeight: PropTypes.string,
- maxWidth: PropTypes.string,
- maxHeight: PropTypes.string,
- base64: PropTypes.string,
- sx: PropTypes.object,
-};
+// Image.propTypes = {
+// shouldRender: PropTypes.bool,
+// src: PropTypes.string,
+// alt: PropTypes.string.isRequired,
+// width: PropTypes.string,
+// height: PropTypes.string,
+// minWidth: PropTypes.string,
+// minHeight: PropTypes.string,
+// maxWidth: PropTypes.string,
+// maxHeight: PropTypes.string,
+// base64: PropTypes.string,
+// sx: PropTypes.object,
+// };
-export default Image;
+// export default Image;
diff --git a/Client/src/Components/StatBox/index.jsx b/Client/src/Components/StatBox/index.jsx
index 58fdb7ebf..30086a221 100644
--- a/Client/src/Components/StatBox/index.jsx
+++ b/Client/src/Components/StatBox/index.jsx
@@ -1,5 +1,6 @@
import { Stack, Typography } from "@mui/material";
-import Image from "../Image";
+//import Image from "../Image";
+import ImageUpload from "../ImageUpload";
import { useTheme } from "@mui/material/styles";
import PropTypes from "prop-types";
import useUtils from "../../Pages/Uptime/Monitors/Hooks/useUtils";
@@ -106,7 +107,7 @@ const StatBox = ({
}}
>
{img && (
- {
gap={theme.spacing(18)}
alignItems="center"
>
-
-
Date: Mon, 10 Feb 2025 18:51:30 -0500
Subject: [PATCH 08/28] remove box material from imageupload component
---
Client/src/Components/ImageUpload/index.jsx | 22 +--------------------
1 file changed, 1 insertion(+), 21 deletions(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index bf4f8aa0e..f9de1d80c 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -1,5 +1,5 @@
import { useState, useRef } from "react";
-import { Box, Button, Stack } from "@mui/material";
+import { Button, Stack } from "@mui/material";
import ImageField from "../Inputs/Image";
import ProgressUpload from "../ProgressBars";
import { formatBytes } from "../../Utils/fileUtils";
@@ -18,15 +18,7 @@ const ImageUpload = ({
currentImage,
theme,
shouldRender = true,
- alt = "Uploaded Image",
- width = "auto",
- height = "auto",
- minWidth = "auto",
- minHeight = "auto",
- maxWidth = "auto",
- maxHeight = "auto",
placeholder,
- sx
}) => {
const [file, setFile] = useState();
const [progress, setProgress] = useState({ value: 0, isLoading: false });
@@ -113,18 +105,6 @@ const ImageUpload = ({
onConfirm={handleUpdatePicture}
isLoading={false}
>
-
Date: Mon, 10 Feb 2025 21:32:30 -0500
Subject: [PATCH 09/28] revert back image component and usage
---
Client/src/Components/Image/index.jsx | 128 +++++++++---------
Client/src/Components/StatBox/index.jsx | 5 +-
.../DistributedUptime/CreateStatus/index.jsx | 5 +-
.../Components/ControlsHeader/index.jsx | 5 +-
4 files changed, 70 insertions(+), 73 deletions(-)
diff --git a/Client/src/Components/Image/index.jsx b/Client/src/Components/Image/index.jsx
index 92707de66..8f30f8a9e 100644
--- a/Client/src/Components/Image/index.jsx
+++ b/Client/src/Components/Image/index.jsx
@@ -1,72 +1,72 @@
-// import { Box } from "@mui/material";
-// import PropTypes from "prop-types";
+import { Box } from "@mui/material";
+import PropTypes from "prop-types";
-// const isValidBase64Image = (data) => {
-// return /^[A-Za-z0-9+/=]+$/.test(data);
-// };
+const isValidBase64Image = (data) => {
+ return /^[A-Za-z0-9+/=]+$/.test(data);
+};
-// const Image = ({
-// shouldRender = true,
-// src,
-// alt,
-// width = "auto",
-// height = "auto",
-// minWidth = "auto",
-// minHeight = "auto",
-// maxWidth = "auto",
-// maxHeight = "auto",
-// base64,
-// placeholder,
-// sx,
-// }) => {
-// if (shouldRender === false) {
-// return null;
-// }
+const Image = ({
+ shouldRender = true,
+ src,
+ alt,
+ width = "auto",
+ height = "auto",
+ minWidth = "auto",
+ minHeight = "auto",
+ maxWidth = "auto",
+ maxHeight = "auto",
+ base64,
+ placeholder,
+ sx,
+}) => {
+ if (shouldRender === false) {
+ return null;
+ }
-// if (typeof src !== "undefined" && typeof base64 !== "undefined") {
-// console.warn("base64 takes precedence over src and overwrites it");
-// }
+ if (typeof src !== "undefined" && typeof base64 !== "undefined") {
+ console.warn("base64 takes precedence over src and overwrites it");
+ }
-// if (typeof base64 !== "undefined" && isValidBase64Image(base64)) {
-// src = `data:image/png;base64,${base64}`;
-// }
+ if (typeof base64 !== "undefined" && isValidBase64Image(base64)) {
+ src = `data:image/png;base64,${base64}`;
+ }
-// if (
-// typeof src === "undefined" &&
-// typeof base64 === "undefined" &&
-// typeof placeholder !== "undefined"
-// ) {
-// src = placeholder;
-// }
+ if (
+ typeof src === "undefined" &&
+ typeof base64 === "undefined" &&
+ typeof placeholder !== "undefined"
+ ) {
+ src = placeholder;
+ }
-// return (
-//
-// );
-// };
+ return (
+
+ );
+};
-// Image.propTypes = {
-// shouldRender: PropTypes.bool,
-// src: PropTypes.string,
-// alt: PropTypes.string.isRequired,
-// width: PropTypes.string,
-// height: PropTypes.string,
-// minWidth: PropTypes.string,
-// minHeight: PropTypes.string,
-// maxWidth: PropTypes.string,
-// maxHeight: PropTypes.string,
-// base64: PropTypes.string,
-// sx: PropTypes.object,
-// };
+Image.propTypes = {
+ shouldRender: PropTypes.bool,
+ src: PropTypes.string,
+ alt: PropTypes.string.isRequired,
+ width: PropTypes.string,
+ height: PropTypes.string,
+ minWidth: PropTypes.string,
+ minHeight: PropTypes.string,
+ maxWidth: PropTypes.string,
+ maxHeight: PropTypes.string,
+ base64: PropTypes.string,
+ sx: PropTypes.object,
+};
-// export default Image;
+export default Image;
diff --git a/Client/src/Components/StatBox/index.jsx b/Client/src/Components/StatBox/index.jsx
index 30086a221..58fdb7ebf 100644
--- a/Client/src/Components/StatBox/index.jsx
+++ b/Client/src/Components/StatBox/index.jsx
@@ -1,6 +1,5 @@
import { Stack, Typography } from "@mui/material";
-//import Image from "../Image";
-import ImageUpload from "../ImageUpload";
+import Image from "../Image";
import { useTheme } from "@mui/material/styles";
import PropTypes from "prop-types";
import useUtils from "../../Pages/Uptime/Monitors/Hooks/useUtils";
@@ -107,7 +106,7 @@ const StatBox = ({
}}
>
{img && (
- {
gap={theme.spacing(18)}
alignItems="center"
>
-
-
Date: Mon, 10 Feb 2025 22:08:04 -0500
Subject: [PATCH 10/28] add drag and drop event handlers to imageupload
---
Client/src/Components/ImageUpload/index.jsx | 33 ++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index f9de1d80c..ff1e5a689 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -1,5 +1,5 @@
import { useState, useRef } from "react";
-import { Button, Stack } from "@mui/material";
+import { Box, Button, Stack } from "@mui/material";
import ImageField from "../Inputs/Image";
import ProgressUpload from "../ProgressBars";
import { formatBytes } from "../../Utils/fileUtils";
@@ -23,6 +23,7 @@ const ImageUpload = ({
const [file, setFile] = useState();
const [progress, setProgress] = useState({ value: 0, isLoading: false });
const [errors, setErrors] = useState({});
+ const [isDragging, setIsDragging] = useState(false);
const intervalRef = useRef(null);
// Handle base64 and placeholder logic
@@ -40,6 +41,15 @@ const ImageUpload = ({
return null;
}
+ const handleDragEnter = () => setIsDragging(true);
+ const handleDragLeave = () => setIsDragging(false);
+ const handleDrop = (event) => {
+ event.preventDefault();
+ setIsDragging(false);
+ handlePicture(event);
+ };
+
+
// Handles image file selection
const handlePicture = (event) => {
const pic = event.target.files[0];
@@ -105,6 +115,27 @@ const ImageUpload = ({
onConfirm={handleUpdatePicture}
isLoading={false}
>
+
Date: Mon, 10 Feb 2025 22:12:23 -0500
Subject: [PATCH 11/28] replace the usage of imagefield
---
Client/src/Components/ImageUpload/index.jsx | 27 ++++++++++-----------
1 file changed, 13 insertions(+), 14 deletions(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index ff1e5a689..a1498d8f6 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -1,6 +1,5 @@
import { useState, useRef } from "react";
import { Box, Button, Stack } from "@mui/material";
-import ImageField from "../Inputs/Image";
import ProgressUpload from "../ProgressBars";
import { formatBytes } from "../../Utils/fileUtils";
import { imageValidation } from "../../Validation/validation";
@@ -136,20 +135,20 @@ const ImageUpload = ({
onDragLeave={handleDragLeave}
onDrop={handleDrop}
/>
-
+ style={{
+ opacity: 0,
+ cursor: "pointer",
+ width: "100%",
+ height: "175px",
+ zIndex: 1,
+ position: "absolute",
+ }}
+ />
+
{progress.isLoading || progress.value !== 0 || errors["picture"] ? (
}
From c8b1fb7faebcab4f43cbc535a8c65aceda916e92 Mon Sep 17 00:00:00 2001
From: Vishnu Sreekumaran Nair <200557136@student.georgianc.on.ca>
Date: Mon, 10 Feb 2025 22:18:16 -0500
Subject: [PATCH 12/28] display upload icon and message in drag drop field
---
Client/src/Components/ImageUpload/index.css | 18 +++++++++
Client/src/Components/ImageUpload/index.jsx | 44 ++++++++++++++++++++-
2 files changed, 60 insertions(+), 2 deletions(-)
create mode 100644 Client/src/Components/ImageUpload/index.css
diff --git a/Client/src/Components/ImageUpload/index.css b/Client/src/Components/ImageUpload/index.css
new file mode 100644
index 000000000..7492f8636
--- /dev/null
+++ b/Client/src/Components/ImageUpload/index.css
@@ -0,0 +1,18 @@
+.MuiStack-root:has(#modal-update-picture) h1.MuiTypography-root {
+ font-weight: 600;
+}
+.image-field-wrapper h2.MuiTypography-root,
+.MuiStack-root:has(#modal-update-picture) button,
+.MuiStack-root:has(#modal-update-picture) h1.MuiTypography-root {
+ font-size: var(--env-var-font-size-medium);
+}
+.image-field-wrapper h2.MuiTypography-root {
+ margin-top: 10px;
+}
+.image-field-wrapper + p.MuiTypography-root {
+ margin-top: 8px;
+}
+.image-field-wrapper + p.MuiTypography-root,
+.image-field-wrapper p.MuiTypography-root {
+ font-size: var(--env-var-font-size-small-plus);
+}
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index a1498d8f6..e45a75198 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -1,10 +1,13 @@
import { useState, useRef } from "react";
-import { Box, Button, Stack } from "@mui/material";
+import { Box, Button, Stack, IconButton, Typography } from "@mui/material";
import ProgressUpload from "../ProgressBars";
import { formatBytes } from "../../Utils/fileUtils";
import { imageValidation } from "../../Validation/validation";
import ImageIcon from "@mui/icons-material/Image";
import {GenericDialog} from "../Dialog/genericDialog";
+import CloudUploadIcon from "@mui/icons-material/CloudUpload";
+import "./index.css";
+
const isValidBase64Image = (data) => {
return /^[A-Za-z0-9+/=]+$/.test(data);
@@ -134,7 +137,7 @@ const ImageUpload = ({
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
- />
+ >
+
+
+
+
+
+
+ Click to upload
+ {" "}
+ or drag and drop
+
+
+ (maximum size: 3MB)
+
+
+
{progress.isLoading || progress.value !== 0 || errors["picture"] ? (
Date: Mon, 10 Feb 2025 22:37:51 -0500
Subject: [PATCH 13/28] replace input with textfield material
---
Client/src/Components/ImageUpload/index.jsx | 24 ++++++++++++++-------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index e45a75198..3ea99a92f 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -1,5 +1,5 @@
import { useState, useRef } from "react";
-import { Box, Button, Stack, IconButton, Typography } from "@mui/material";
+import { Box, Button, Stack, IconButton, TextField , Typography } from "@mui/material";
import ProgressUpload from "../ProgressBars";
import { formatBytes } from "../../Utils/fileUtils";
import { imageValidation } from "../../Validation/validation";
@@ -108,6 +108,7 @@ const ImageUpload = ({
return (
e.preventDefault()}
>
-
Date: Mon, 10 Feb 2025 22:40:48 -0500
Subject: [PATCH 14/28] add validation for files
---
Client/src/Components/ImageUpload/index.jsx | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index 3ea99a92f..83d777172 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -55,17 +55,20 @@ const ImageUpload = ({
// Handles image file selection
const handlePicture = (event) => {
const pic = event.target.files[0];
+ if (!pic) return;
+
+ // Validate file type and size
let error = validateField({ type: pic.type, size: pic.size }, imageValidation);
if (error) return;
-
- setProgress((prev) => ({ ...prev, isLoading: true }));
+
+ setProgress({ value: 0, isLoading: true });
setFile({
src: URL.createObjectURL(pic),
name: pic.name,
size: formatBytes(pic.size),
delete: false,
});
-
+
// Simulate upload progress
intervalRef.current = setInterval(() => {
const buffer = 12;
@@ -78,6 +81,7 @@ const ImageUpload = ({
});
}, 120);
};
+
// Validates input against provided schema and updates error state
const validateField = (toValidate, schema, name = "picture") => {
From 419d982322005877139cc714328a8ec49acc5f36 Mon Sep 17 00:00:00 2001
From: Vishnu Sreekumaran Nair <200557136@student.georgianc.on.ca>
Date: Mon, 10 Feb 2025 23:50:25 -0500
Subject: [PATCH 15/28] fix preview image display issue
---
Client/src/Components/ImageUpload/index.jsx | 178 +++++++++++---------
1 file changed, 102 insertions(+), 76 deletions(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index 83d777172..f936dc15a 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -6,6 +6,7 @@ import { imageValidation } from "../../Validation/validation";
import ImageIcon from "@mui/icons-material/Image";
import {GenericDialog} from "../Dialog/genericDialog";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
+import { checkImage } from "../../Utils/fileUtils";
import "./index.css";
@@ -115,7 +116,7 @@ const ImageUpload = ({
id="modal-update-picture"
open={open}
onClose={onClose}
- theme={theme} // Pass the theme prop
+ theme={theme}
title={"Upload Image"}
description={"Select an image to upload."}
confirmationButtonLabel={"Update"}
@@ -123,85 +124,109 @@ const ImageUpload = ({
isLoading={false}
>
e.preventDefault()}
- >
- e.preventDefault()}
+>
+
+ {/* ✅ FILE INPUT FIXED - Ensuring it covers entire upload area */}
+
-
-
+
+ {!checkImage(file?.src || currentImage) || progress.isLoading ? (
+ <>
+
+
+
+
+
+
+ Click to upload
+ {" "}
+ or drag and drop
+
+
+ (maximum size: 3MB)
+
+
+ >
+ ) : (
+
-
-
-
-
- Click to upload
- {" "}
- or drag and drop
-
-
- (maximum size: 3MB)
-
-
-
+ />
+ )}
+
- {progress.isLoading || progress.value !== 0 || errors["picture"] ? (
+
+ {progress.isLoading || progress.value !== 0 || errors["picture"] ? (
}
label={file?.name}
@@ -210,15 +235,14 @@ const ImageUpload = ({
onClick={removePicture}
error={errors["picture"]}
/>
- ) : (
- ""
- )}
-
+ >
-
-
+
+
+
+
);
};
From b3599c117f20853c54a3ffbc8579a72ffa009824 Mon Sep 17 00:00:00 2001
From: Vishnu Sreekumaran Nair <200557136@student.georgianc.on.ca>
Date: Tue, 11 Feb 2025 00:07:32 -0500
Subject: [PATCH 16/28] remove imagefield component and replace the usages with
imageupload
---
Client/src/Components/ImageUpload/index.jsx | 282 +++++++++---------
Client/src/Components/Inputs/Image/index.css | 18 --
Client/src/Components/Inputs/Image/index.jsx | 175 -----------
.../Create/Components/Tabs/Settings.jsx | 4 +-
4 files changed, 141 insertions(+), 338 deletions(-)
delete mode 100644 Client/src/Components/Inputs/Image/index.css
delete mode 100644 Client/src/Components/Inputs/Image/index.jsx
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index f936dc15a..3732d920d 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -113,160 +113,156 @@ const ImageUpload = ({
return (
- e.preventDefault()}
->
-
- {/* ✅ FILE INPUT FIXED - Ensuring it covers entire upload area */}
-
-
- {!checkImage(file?.src || currentImage) || progress.isLoading ? (
- <>
-
-
-
-
-
-
- Click to upload
- {" "}
- or drag and drop
-
-
- (maximum size: 3MB)
-
-
- >
- ) : (
+ id="modal-update-picture"
+ open={open}
+ onClose={onClose}
+ theme={theme}
+ title={"Upload Image"}
+ description={"Select an image to upload."}
+ confirmationButtonLabel={"Update"}
+ onConfirm={handleUpdatePicture}
+ isLoading={false}
+ >
e.preventDefault()}
+ >
+
- )}
-
+ {!checkImage(file?.src || currentImage) || progress.isLoading ? (
+ <>
+
+
+
+
+
+
+ Click to upload
+ {" "}
+ or drag and drop
+
+
+ (maximum size: 3MB)
+
+
+ >
+ ) : (
+
+ )}
+
- {progress.isLoading || progress.value !== 0 || errors["picture"] ? (
- }
- label={file?.name}
- size={file?.size}
- progress={progress.value}
- onClick={removePicture}
- error={errors["picture"]}
- />
- ) : null}
-
-
-
-
-
-
+ {progress.isLoading || progress.value !== 0 || errors["picture"] ? (
+ }
+ label={file?.name}
+ size={file?.size}
+ progress={progress.value}
+ onClick={removePicture}
+ error={errors["picture"]}
+ />
+ ) : null}
+
+
+
+
+
);
};
diff --git a/Client/src/Components/Inputs/Image/index.css b/Client/src/Components/Inputs/Image/index.css
deleted file mode 100644
index 7492f8636..000000000
--- a/Client/src/Components/Inputs/Image/index.css
+++ /dev/null
@@ -1,18 +0,0 @@
-.MuiStack-root:has(#modal-update-picture) h1.MuiTypography-root {
- font-weight: 600;
-}
-.image-field-wrapper h2.MuiTypography-root,
-.MuiStack-root:has(#modal-update-picture) button,
-.MuiStack-root:has(#modal-update-picture) h1.MuiTypography-root {
- font-size: var(--env-var-font-size-medium);
-}
-.image-field-wrapper h2.MuiTypography-root {
- margin-top: 10px;
-}
-.image-field-wrapper + p.MuiTypography-root {
- margin-top: 8px;
-}
-.image-field-wrapper + p.MuiTypography-root,
-.image-field-wrapper p.MuiTypography-root {
- font-size: var(--env-var-font-size-small-plus);
-}
diff --git a/Client/src/Components/Inputs/Image/index.jsx b/Client/src/Components/Inputs/Image/index.jsx
deleted file mode 100644
index 971f17c00..000000000
--- a/Client/src/Components/Inputs/Image/index.jsx
+++ /dev/null
@@ -1,175 +0,0 @@
-import React, { useState } from "react";
-import PropTypes from "prop-types";
-import { Box, IconButton, Stack, TextField, Typography } from "@mui/material";
-import { useTheme } from "@emotion/react";
-import CloudUploadIcon from "@mui/icons-material/CloudUpload";
-import "./index.css";
-import { checkImage } from "../../../Utils/fileUtils";
-
-/**
- * @param {Object} props - The component props.
- * @param {string} props.id - The unique identifier for the input field.
- * @param {string} props.src - The URL of the image to display.
- * @param {function} props.onChange - The function to handle file input change.
- * @param {boolean} props.isRound - Whether the shape of the image to display is round.
- * @param {string} props.maxSize - Custom message for the max uploaded file size
- * @returns {JSX.Element} The rendered component.
- */
-
-const ImageField = ({ id, src, loading, onChange, error, isRound = true, maxSize }) => {
- const theme = useTheme();
- const error_border_style = error ? { borderColor: theme.palette.error.main } : {};
-
- const roundShape = isRound ? { borderRadius: "50%" } : {};
-
- const [isDragging, setIsDragging] = useState(false);
- const handleDragEnter = () => {
- setIsDragging(true);
- };
- const handleDragLeave = () => {
- setIsDragging(false);
- };
-
- return (
- <>
- {!checkImage(src) || loading ? (
- <>
-
-
-
-
-
-
-
-
- Click to upload
- {" "}
- or drag and drop
-
-
- (maximum size: {maxSize ?? "3MB"})
-
-
-
-
- Supported formats: JPG, PNG
-
- {error && (
-
- {error}
-
- )}
- >
- ) : (
-
-
-
- )}
- >
- );
-};
-
-ImageField.propTypes = {
- id: PropTypes.string.isRequired,
- src: PropTypes.string,
- onChange: PropTypes.func.isRequired,
- isRound: PropTypes.bool,
- maxSize: PropTypes.string,
-};
-
-export default ImageField;
diff --git a/Client/src/Pages/StatusPage/Create/Components/Tabs/Settings.jsx b/Client/src/Pages/StatusPage/Create/Components/Tabs/Settings.jsx
index e4fd56ebb..28b5e5998 100644
--- a/Client/src/Pages/StatusPage/Create/Components/Tabs/Settings.jsx
+++ b/Client/src/Pages/StatusPage/Create/Components/Tabs/Settings.jsx
@@ -5,7 +5,7 @@ import ConfigBox from "../../../../../Components/ConfigBox";
import Checkbox from "../../../../../Components/Inputs/Checkbox";
import TextInput from "../../../../../Components/Inputs/TextInput";
import Select from "../../../../../Components/Inputs/Select";
-import ImageField from "../../../../../Components/Inputs/Image";
+import ImageUpload from "../../../../../Components/ImageUpload";
import ColorPicker from "../../../../../Components/Inputs/ColorPicker";
import Progress from "../Progress";
@@ -101,7 +101,7 @@ const TabSettings = ({
-
Date: Tue, 11 Feb 2025 00:30:04 -0500
Subject: [PATCH 17/28] fix image reupload issue by triggerring state change
---
Client/src/Components/ImageUpload/index.jsx | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index 3732d920d..9324708ff 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -9,7 +9,6 @@ import CloudUploadIcon from "@mui/icons-material/CloudUpload";
import { checkImage } from "../../Utils/fileUtils";
import "./index.css";
-
const isValidBase64Image = (data) => {
return /^[A-Za-z0-9+/=]+$/.test(data);
};
@@ -52,11 +51,13 @@ const ImageUpload = ({
handlePicture(event);
};
-
// Handles image file selection
const handlePicture = (event) => {
const pic = event.target.files[0];
if (!pic) return;
+
+ event.target.value = "";
+ setFile(null);
// Validate file type and size
let error = validateField({ type: pic.type, size: pic.size }, imageValidation);
@@ -82,7 +83,6 @@ const ImageUpload = ({
});
}, 120);
};
-
// Validates input against provided schema and updates error state
const validateField = (toValidate, schema, name = "picture") => {
@@ -99,7 +99,7 @@ const ImageUpload = ({
// Resets picture-related states and clears interval
const removePicture = () => {
errors["picture"] && setErrors((prev) => ({ ...prev, picture: undefined }));
- setFile({ delete: true });
+ setFile(null);
clearInterval(intervalRef.current);
setProgress({ value: 0, isLoading: false });
};
@@ -223,7 +223,6 @@ const ImageUpload = ({
)}
-
{progress.isLoading || progress.value !== 0 || errors["picture"] ? (
}
From 5b97b1995a3ff04710502a6322253d59da1b4fb9 Mon Sep 17 00:00:00 2001
From: Vishnu Sreekumaran Nair <200557136@student.georgianc.on.ca>
Date: Tue, 11 Feb 2025 01:59:05 -0500
Subject: [PATCH 18/28] set drag and drop event handlers correctly
---
Client/src/Components/ImageUpload/index.jsx | 31 ++++++++++---------
.../TabPanels/Account/ProfilePanel.jsx | 2 +-
2 files changed, 18 insertions(+), 15 deletions(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index 9324708ff..3c3388eab 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -13,15 +13,7 @@ const isValidBase64Image = (data) => {
return /^[A-Za-z0-9+/=]+$/.test(data);
};
-const ImageUpload = ({
- open,
- onClose,
- onUpdate,
- currentImage,
- theme,
- shouldRender = true,
- placeholder,
- }) => {
+const ImageUpload = ({ open, onClose, onUpdate, currentImage, theme, shouldRender = true, placeholder,}) => {
const [file, setFile] = useState();
const [progress, setProgress] = useState({ value: 0, isLoading: false });
const [errors, setErrors] = useState({});
@@ -43,13 +35,24 @@ const ImageUpload = ({
return null;
}
- const handleDragEnter = () => setIsDragging(true);
- const handleDragLeave = () => setIsDragging(false);
- const handleDrop = (event) => {
+ const handleDragEnter = (event) => {
+ event.preventDefault();
+ setIsDragging(true);
+};
+
+const handleDragLeave = (event) => {
event.preventDefault();
setIsDragging(false);
- handlePicture(event);
- };
+};
+
+const handleDrop = (event) => {
+ event.preventDefault();
+ setIsDragging(false);
+
+ if (event.dataTransfer.files.length > 0) {
+ handlePicture({ target: { files: event.dataTransfer.files } });
+ }
+};
// Handles image file selection
const handlePicture = (event) => {
diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx
index 38c26fb28..e58bb3bc0 100644
--- a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx
+++ b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx
@@ -12,7 +12,7 @@ import { createToast } from "../../../Utils/toastUtils";
import { logger } from "../../../Utils/Logger";
import LoadingButton from "@mui/lab/LoadingButton";
import { GenericDialog } from "../../Dialog/genericDialog";
-import ImageUpload from "../../ImageUpload"; // Import the new ImageUpload component
+import ImageUpload from "../../ImageUpload";
const ProfilePanel = () => {
const theme = useTheme();
From eedfdd366b94095ed1cdaf3f6d28582a3fa6eaa9 Mon Sep 17 00:00:00 2001
From: Vishnu Sreekumaran Nair <200557136@student.georgianc.on.ca>
Date: Tue, 11 Feb 2025 02:02:00 -0500
Subject: [PATCH 19/28] add prop to handle validation error
---
Client/src/Components/ImageUpload/index.jsx | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index 3c3388eab..bdaeaaf78 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -91,12 +91,16 @@ const handleDrop = (event) => {
const validateField = (toValidate, schema, name = "picture") => {
const { error } = schema.validate(toValidate, { abortEarly: false });
setErrors((prev) => {
- const prevErrors = { ...prev };
- if (error) prevErrors[name] = error.details[0].message;
- else delete prevErrors[name];
- return prevErrors;
+ const prevErrors = { ...prev };
+ if (error) {
+ prevErrors[name] = error.details[0].message;
+ if (onError) onError(error.details[0].message);
+ } else {
+ delete prevErrors[name];
+ }
+ return prevErrors;
});
- if (error) return true;
+ return !!error;
};
// Resets picture-related states and clears interval
From 808cc9240bd029deb76b4b1dd802fdd245e8f199 Mon Sep 17 00:00:00 2001
From: Vishnu Sreekumaran Nair <200557136@student.georgianc.on.ca>
Date: Tue, 11 Feb 2025 02:13:35 -0500
Subject: [PATCH 20/28] added support for props to standardize API
---
Client/src/Components/ImageUpload/index.jsx | 55 +++++++++++++--------
1 file changed, 35 insertions(+), 20 deletions(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index bdaeaaf78..45265ec9c 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -13,7 +13,8 @@ const isValidBase64Image = (data) => {
return /^[A-Za-z0-9+/=]+$/.test(data);
};
-const ImageUpload = ({ open, onClose, onUpdate, currentImage, theme, shouldRender = true, placeholder,}) => {
+const ImageUpload = ({ open, onClose, onUpdate, value, currentImage = value, theme, shouldRender = true, placeholder, maxSize, acceptedTypes, previewSize = 150, onError,}) => {
+
const [file, setFile] = useState();
const [progress, setProgress] = useState({ value: 0, isLoading: false });
const [errors, setErrors] = useState({});
@@ -61,31 +62,45 @@ const handleDrop = (event) => {
event.target.value = "";
setFile(null);
-
- // Validate file type and size
+
+ if (maxSize && pic.size > maxSize) {
+ const errorMsg = `File size exceeds ${formatBytes(maxSize)}`;
+ setErrors({ picture: errorMsg });
+ if (onError) onError(errorMsg);
+ return;
+ }
+
+ if (acceptedTypes && !acceptedTypes.includes(pic.type)) {
+ const errorMsg = `File type not supported. Allowed: ${acceptedTypes.join(", ")}`;
+ setErrors({ picture: errorMsg });
+ if (onError) onError(errorMsg);
+ return;
+ }
+
+ // Validate file type and size using schema
let error = validateField({ type: pic.type, size: pic.size }, imageValidation);
if (error) return;
-
+
setProgress({ value: 0, isLoading: true });
setFile({
- src: URL.createObjectURL(pic),
- name: pic.name,
- size: formatBytes(pic.size),
- delete: false,
+ src: URL.createObjectURL(pic),
+ name: pic.name,
+ size: formatBytes(pic.size),
+ delete: false,
});
-
+
// Simulate upload progress
intervalRef.current = setInterval(() => {
- const buffer = 12;
- setProgress((prev) => {
- if (prev.value + buffer >= 100) {
- clearInterval(intervalRef.current);
- return { value: 100, isLoading: false };
- }
- return { ...prev, value: prev.value + buffer };
- });
+ const buffer = 12;
+ setProgress((prev) => {
+ if (prev.value + buffer >= 100) {
+ clearInterval(intervalRef.current);
+ return { value: 100, isLoading: false };
+ }
+ return { ...prev, value: prev.value + buffer };
+ });
}, 120);
- };
+ };
// Validates input against provided schema and updates error state
const validateField = (toValidate, schema, name = "picture") => {
@@ -215,8 +230,8 @@ const handleDrop = (event) => {
) : (
Date: Wed, 12 Feb 2025 19:31:54 -0500
Subject: [PATCH 21/28] remove index.css file
---
Client/src/Components/ImageUpload/index.css | 18 ------------------
Client/src/Components/ImageUpload/index.jsx | 1 -
2 files changed, 19 deletions(-)
delete mode 100644 Client/src/Components/ImageUpload/index.css
diff --git a/Client/src/Components/ImageUpload/index.css b/Client/src/Components/ImageUpload/index.css
deleted file mode 100644
index 7492f8636..000000000
--- a/Client/src/Components/ImageUpload/index.css
+++ /dev/null
@@ -1,18 +0,0 @@
-.MuiStack-root:has(#modal-update-picture) h1.MuiTypography-root {
- font-weight: 600;
-}
-.image-field-wrapper h2.MuiTypography-root,
-.MuiStack-root:has(#modal-update-picture) button,
-.MuiStack-root:has(#modal-update-picture) h1.MuiTypography-root {
- font-size: var(--env-var-font-size-medium);
-}
-.image-field-wrapper h2.MuiTypography-root {
- margin-top: 10px;
-}
-.image-field-wrapper + p.MuiTypography-root {
- margin-top: 8px;
-}
-.image-field-wrapper + p.MuiTypography-root,
-.image-field-wrapper p.MuiTypography-root {
- font-size: var(--env-var-font-size-small-plus);
-}
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index 45265ec9c..2e90918e1 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -7,7 +7,6 @@ import ImageIcon from "@mui/icons-material/Image";
import {GenericDialog} from "../Dialog/genericDialog";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
import { checkImage } from "../../Utils/fileUtils";
-import "./index.css";
const isValidBase64Image = (data) => {
return /^[A-Za-z0-9+/=]+$/.test(data);
From 1368ffa13e715cd97994dca722e10b57ca979048 Mon Sep 17 00:00:00 2001
From: Vishnu Sreekumaran Nair <200557136@student.georgianc.on.ca>
Date: Wed, 12 Feb 2025 21:31:26 -0500
Subject: [PATCH 22/28] remove internal error handling from imageupload
component
---
Client/src/Components/ImageUpload/index.jsx | 90 ++++++++++---------
.../TabPanels/Account/ProfilePanel.jsx | 18 ++--
2 files changed, 59 insertions(+), 49 deletions(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index 2e90918e1..a8d060276 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -1,5 +1,5 @@
import { useState, useRef } from "react";
-import { Box, Button, Stack, IconButton, TextField , Typography } from "@mui/material";
+import { Box, Button, Stack, IconButton , Typography } from "@mui/material";
import ProgressUpload from "../ProgressBars";
import { formatBytes } from "../../Utils/fileUtils";
import { imageValidation } from "../../Validation/validation";
@@ -7,29 +7,34 @@ import ImageIcon from "@mui/icons-material/Image";
import {GenericDialog} from "../Dialog/genericDialog";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
import { checkImage } from "../../Utils/fileUtils";
+import { useTheme } from "@mui/material/styles";
const isValidBase64Image = (data) => {
return /^[A-Za-z0-9+/=]+$/.test(data);
};
-const ImageUpload = ({ open, onClose, onUpdate, value, currentImage = value, theme, shouldRender = true, placeholder, maxSize, acceptedTypes, previewSize = 150, onError,}) => {
+const ImageUpload = ({ open, onClose, onUpdate, shouldRender = true, placeholder, maxSize, acceptedTypes, previewSize = 150, setErrors, errors}) => {
const [file, setFile] = useState();
const [progress, setProgress] = useState({ value: 0, isLoading: false });
- const [errors, setErrors] = useState({});
const [isDragging, setIsDragging] = useState(false);
const intervalRef = useRef(null);
-
+ const theme = useTheme();
+ const UPLOAD_PROGRESS_INCREMENT = 12; // Controls the speed of upload progress
+ const UPLOAD_PROGRESS_INTERVAL = 120; // Controls how often progress updates (milliseconds)
+ const UPLOAD_COMPLETE = 100; // Represents the max progress percentage
+
// Handle base64 and placeholder logic
- let imageSrc = currentImage;
+ let imageSrc = placeholder || ""; // Default to placeholder or empty string
- if (typeof file?.src !== "undefined") {
- imageSrc = file.src;
- } else if (typeof currentImage !== "undefined" && isValidBase64Image(currentImage)) {
- imageSrc = `data:image/png;base64,${currentImage}`;
- } else if (typeof placeholder !== "undefined") {
- imageSrc = placeholder;
- }
+ if (file?.src) {
+ imageSrc = file.src; // Use uploaded file's preview
+ } else if (placeholder && isValidBase64Image(placeholder)) {
+ imageSrc = `data:image/png;base64,${placeholder}`; // Convert base64 string if valid
+ } else {
+ imageSrc = ""; // Ensure it's an empty string instead of "undefined"
+ }
+
if (shouldRender === false) {
return null;
@@ -64,14 +69,14 @@ const handleDrop = (event) => {
if (maxSize && pic.size > maxSize) {
const errorMsg = `File size exceeds ${formatBytes(maxSize)}`;
- setErrors({ picture: errorMsg });
+ if (setErrors) setErrors((prev) => ({ ...prev, picture: errorMsg }));
if (onError) onError(errorMsg);
return;
}
if (acceptedTypes && !acceptedTypes.includes(pic.type)) {
const errorMsg = `File type not supported. Allowed: ${acceptedTypes.join(", ")}`;
- setErrors({ picture: errorMsg });
+ if (setErrors) setErrors((prev) => ({ ...prev, picture: errorMsg }));
if (onError) onError(errorMsg);
return;
}
@@ -90,30 +95,31 @@ const handleDrop = (event) => {
// Simulate upload progress
intervalRef.current = setInterval(() => {
- const buffer = 12;
setProgress((prev) => {
- if (prev.value + buffer >= 100) {
+ const nextValue = prev.value + UPLOAD_PROGRESS_INCREMENT;
+ if (nextValue >= UPLOAD_COMPLETE) {
clearInterval(intervalRef.current);
- return { value: 100, isLoading: false };
+ return { value: UPLOAD_COMPLETE, isLoading: false };
}
- return { ...prev, value: prev.value + buffer };
+ return { ...prev, value: nextValue };
});
- }, 120);
- };
+ }, UPLOAD_PROGRESS_INTERVAL);
+ };
// Validates input against provided schema and updates error state
const validateField = (toValidate, schema, name = "picture") => {
const { error } = schema.validate(toValidate, { abortEarly: false });
- setErrors((prev) => {
- const prevErrors = { ...prev };
- if (error) {
- prevErrors[name] = error.details[0].message;
- if (onError) onError(error.details[0].message);
- } else {
- delete prevErrors[name];
- }
- return prevErrors;
- });
+ if (setErrors) {
+ setErrors((prev) => {
+ const prevErrors = { ...prev };
+ if (error) {
+ prevErrors[name] = error.details[0].message;
+ } else {
+ delete prevErrors[name];
+ }
+ return prevErrors;
+ });
+ }
return !!error;
};
@@ -127,8 +133,10 @@ const handleDrop = (event) => {
// Updates the profile picture and closes the modal
const handleUpdatePicture = () => {
+ if (file?.src) {
+ onUpdate(file.src);
+ }
setProgress({ value: 0, isLoading: false });
- onUpdate(file.src); // Pass the new image URL to the parent component
onClose(); // Close the modal
};
@@ -137,11 +145,11 @@ const handleDrop = (event) => {
id="modal-update-picture"
open={open}
onClose={onClose}
- theme={theme}
title={"Upload Image"}
description={"Select an image to upload."}
confirmationButtonLabel={"Update"}
onConfirm={handleUpdatePicture}
+ theme={theme}
isLoading={false}
>
{
}}
/>
- {!checkImage(file?.src || currentImage) || progress.isLoading ? (
+ {!checkImage(imageSrc) || progress.isLoading ? (
<>
{
width: `${previewSize}px`,
height: `${previewSize}px`,
overflow: "hidden",
- backgroundImage: `url(${file?.src || currentImage})`,
+ backgroundImage: imageSrc ? `url(${imageSrc})` : "none",
backgroundSize: "cover",
backgroundPosition: "center",
borderRadius: "50%",
@@ -244,21 +252,21 @@ const handleDrop = (event) => {
)}
- {progress.isLoading || progress.value !== 0 || errors["picture"] ? (
+ {progress.isLoading || progress.value !== 0 || errors?.picture ? (
}
label={file?.name}
- size={file?.size}
+ size={file?.size || "0 KB"}
progress={progress.value}
onClick={removePicture}
- error={errors["picture"]}
+ error={errors?.picture}
/>
) : null}
>
From 7657cd50e9ad13e9988d410a95bfcf3527845ab5 Mon Sep 17 00:00:00 2001
From: Vishnu Sreekumaran Nair <200557136@student.georgianc.on.ca>
Date: Wed, 12 Feb 2025 22:46:30 -0500
Subject: [PATCH 26/28] replace hardcoded strings with i18next
---
Client/src/Components/ImageUpload/index.jsx | 8 +++++---
Client/src/locales/gb.json | 6 +++++-
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index a04de6d76..d1523048c 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -8,6 +8,7 @@ import {GenericDialog} from "../Dialog/genericDialog";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
import { checkImage } from "../../Utils/fileUtils";
import { useTheme } from "@mui/material/styles";
+import { useTranslation } from "react-i18next";
const isValidBase64Image = (data) => {
return /^[A-Za-z0-9+/=]+$/.test(data);
@@ -25,6 +26,7 @@ const ImageUpload = ({ open, onClose, onUpdate, placeholder, maxSize = DEFAULT_M
const UPLOAD_PROGRESS_INCREMENT = 12; // Controls the speed of upload progress
const UPLOAD_PROGRESS_INTERVAL = 120; // Controls how often progress updates (milliseconds)
const UPLOAD_COMPLETE = 100; // Represents the max progress percentage
+ const { t } = useTranslation();
// Handle base64 and placeholder logic
let imageSrc = placeholder || ""; // Default to placeholder or empty string
@@ -132,9 +134,9 @@ const ImageUpload = ({ open, onClose, onUpdate, placeholder, maxSize = DEFAULT_M
id="modal-update-picture"
open={open}
onClose={onClose}
- title={"Upload Image"}
- description={"Select an image to upload."}
- confirmationButtonLabel={"Update"}
+ title={t("uploadImage")}
+ description={t("selectImage")}
+ confirmationButtonLabel={t("update")}
onConfirm={handleUpdatePicture}
theme={theme}
isLoading={false}
diff --git a/Client/src/locales/gb.json b/Client/src/locales/gb.json
index 03dd02c35..ca217955f 100644
--- a/Client/src/locales/gb.json
+++ b/Client/src/locales/gb.json
@@ -55,5 +55,9 @@
"authRegisterCreateAccount": "Create your account to get started",
"authRegisterCreateSuperAdminAccount": "Create your Super admin account to get started",
"authRegisterSignUpWithEmail": "Sign up with Email",
- "authRegisterBySigningUp": "By signing up, you agree to our"
+ "authRegisterBySigningUp": "By signing up, you agree to our",
+ "uploadImage": "Upload image",
+ "selectImage": "Select an image to upload.",
+ "update": "Update"
+
}
\ No newline at end of file
From ea18f50af2872d642f46e568f8287b8e4a77d0fe Mon Sep 17 00:00:00 2001
From: Vishnu Sreekumaran Nair <200557136@student.georgianc.on.ca>
Date: Wed, 12 Feb 2025 22:52:37 -0500
Subject: [PATCH 27/28] add jsdoc for imageupload component
---
Client/src/Components/ImageUpload/index.jsx | 23 +++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index d1523048c..a7b807cf5 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -10,6 +10,29 @@ import { checkImage } from "../../Utils/fileUtils";
import { useTheme } from "@mui/material/styles";
import { useTranslation } from "react-i18next";
+/**
+ * @component
+ * @param {Object} props - Component properties.
+ * @param {boolean} props.open - Controls whether the upload modal is open.
+ * @param {function} props.onClose - Function to close the modal.
+ * @param {function} props.onUpdate - Callback function triggered when an image is successfully uploaded.
+ * @param {string} [props.placeholder] - Optional placeholder image (URL or base64).
+ * @param {number} [props.maxSize=3145728] - Maximum allowed file size in bytes (default: 3MB).
+ * @param {string[]} [props.acceptedTypes] - List of allowed image MIME types (e.g., `["image/png", "image/jpeg"]`).
+ * @param {number} [props.previewSize=150] - Size of the image preview in pixels.
+ * @param {function} [props.setErrors] - Function to set error messages in the parent component.
+ * @param {Object} [props.errors={}] - Object containing validation errors.
+ *
+ * @example
+ * console.log("Image uploaded:", imageUrl)}
+ * maxSize={5 * 1024 * 1024} // 5MB
+ * acceptedTypes={["image/png", "image/jpeg"]}
+ * />
+ */
+
const isValidBase64Image = (data) => {
return /^[A-Za-z0-9+/=]+$/.test(data);
};
From a4f54f73d7f84e0fa1cc9421a6e6a08301074ea9 Mon Sep 17 00:00:00 2001
From: Vishnu Sreekumaran Nair <200557136@student.georgianc.on.ca>
Date: Wed, 12 Feb 2025 23:05:22 -0500
Subject: [PATCH 28/28] error handling prop bug fix
---
Client/src/Components/ImageUpload/index.jsx | 2 --
1 file changed, 2 deletions(-)
diff --git a/Client/src/Components/ImageUpload/index.jsx b/Client/src/Components/ImageUpload/index.jsx
index a7b807cf5..c382667fd 100644
--- a/Client/src/Components/ImageUpload/index.jsx
+++ b/Client/src/Components/ImageUpload/index.jsx
@@ -82,14 +82,12 @@ const ImageUpload = ({ open, onClose, onUpdate, placeholder, maxSize = DEFAULT_M
if (maxSize && pic.size > maxSize) {
const errorMsg = `File size exceeds ${formatBytes(maxSize)}`;
if (setErrors) setErrors?.((prev) => ({ ...prev, picture: errorMsg }));
- if (onError) onError(errorMsg);
return;
}
if (acceptedTypes && !acceptedTypes.includes(pic.type)) {
const errorMsg = `File type not supported. Allowed: ${acceptedTypes.join(", ")}`;
if (setErrors) setErrors?.((prev) => ({ ...prev, picture: errorMsg }));
- if (onError) onError(errorMsg);
return;
}