diff --git a/Client/src/Components/Inputs/Image/index.jsx b/Client/src/Components/Inputs/Image/index.jsx
index 9d258eaad..d1404e94c 100644
--- a/Client/src/Components/Inputs/Image/index.jsx
+++ b/Client/src/Components/Inputs/Image/index.jsx
@@ -14,8 +14,9 @@ import { checkImage } from "../../../Utils/fileUtils";
* @returns {JSX.Element} The rendered component.
*/
-const ImageField = ({ id, src, loading, onChange }) => {
+const ImageField = ({ id, src, loading, onChange, error }) => {
const theme = useTheme();
+ const error_border_style = error? {borderColor: theme.palette.error.main}: {}
const [isDragging, setIsDragging] = useState(false);
const handleDragEnter = () => {
@@ -28,7 +29,7 @@ const ImageField = ({ id, src, loading, onChange }) => {
return (
<>
{!checkImage(src) || loading ? (
- <>
+ <>
{
borderColor: theme.palette.primary.main,
backgroundColor: "hsl(215, 87%, 51%, 0.05)",
},
+ ...error_border_style
}}
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
@@ -62,7 +64,7 @@ const ImageField = ({ id, src, loading, onChange }) => {
cursor: "pointer",
maxWidth: "500px",
minHeight: "175px",
- zIndex: 1
+ zIndex: 1,
},
"& fieldset": {
padding: 0,
@@ -79,7 +81,7 @@ const ImageField = ({ id, src, loading, onChange }) => {
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
- zIndex:0,
+ zIndex: 0,
width: "100%",
}}
>
@@ -114,7 +116,7 @@ const ImageField = ({ id, src, loading, onChange }) => {
>
(maximum size: 3MB)
-
+
{
>
Supported formats: JPG, PNG
+ {error && (
+
+ {error}
+
+ )}
>
) : (
{
const theme = useTheme();
const dispatch = useDispatch();
const [errors, setErrors] = useState({});
- const [file, setFile] = useState();
+ const [logo, setLogo] = useState();
+ const [progress, setProgress] = useState({ value: 0, isLoading: false });
+ const intervalRef = useRef(null);
// Local state for form data, errors, and file handling
const [localData, setLocalData] = useState({
companyName: "",
url: "",
timezone: "America/Toronto",
- color: "#4169E1",
+ color: "#4169E1",
theme: "light",
//which fields matching below?
publish: false,
- file: null
+ logo: null,
});
- const handleChange = (event, appendedId) => {
+ // Clears specific error from errors state
+ const clearError = (err) => {
+ setErrors((prev) => {
+ const updatedErrors = { ...prev };
+ if (updatedErrors[err]) delete updatedErrors[err];
+ return updatedErrors;
+ });
+ };
+ const removeLogo = () => {
+ errors["logo"] && clearError("logo");
+ setLogo({});
+ // interrupt interval if image upload is canceled prior to completing the process
+ clearInterval(intervalRef.current);
+ setProgress({ value: 0, isLoading: false });
+ };
+
+ // Updates profile image displayed on UI
+ const handleUpdateLogo = () => {
+ setProgress({ value: 0, isLoading: false });
+ setLocalData((prev) => ({
+ ...prev,
+ logo: logo.src,
+ }));
+ };
+
+ const handleChange = (event) => {
event.preventDefault();
const { value, id } = event.target;
- let name = appendedId ?? idMap[id] ?? id;
setLocalData((prev) => ({
- ...prev,
- [name]: value,
- }));
- }
-
+ ...prev,
+ [id]: value,
+ }));
+ };
const handleSubmit = () => {};
- const handleBlur = () => {};
+ const handleBlur = (event) => {
+ event.preventDefault();
+ const { value, id } = event.target;
+ const { error } = publicPageGeneralSettingsValidation.validate(
+ { [id]: value },
+ {
+ abortEarly: false,
+ }
+ );
- const handlePicture = (event) => {
+ setErrors((prev) => {
+ return buildErrors(prev, id, error);
+ });
+ };
+
+ const validateField = (toValidate, schema, name = "logo") => {
+ const { error } = schema.validate(toValidate, { abortEarly: false });
+ setErrors((prev) => {
+ let prevErrors = { ...prev };
+ if (error) prevErrors[name] = error?.details[0].message;
+ else delete prevErrors[name];
+ return prevErrors;
+ });
+ if (error) return true;
+ };
+
+ const handleLogo = (event) => {
const pic = event.target.files[0];
+ let error = validateField({ type: pic?.type, size: pic?.size }, logoImageValidation);
+ if (error) return;
+
+ setProgress((prev) => ({ ...prev, isLoading: true }));
+ setLogo({
+ src: URL.createObjectURL(pic),
+ name: pic.name,
+ size: formatBytes(pic.size),
+ });
+
+ 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);
};
return (
@@ -79,7 +153,7 @@ const GeneralSettingsPanel = () => {
id="published-to-public"
label={`Published and visible to the public`}
isChecked={localData.publish}
- value={localData.publish}
+ value={"localData.publish"}
onChange={handleChange}
onBlur={handleBlur}
/>
@@ -97,19 +171,24 @@ const GeneralSettingsPanel = () => {
@@ -128,29 +207,71 @@ const GeneralSettingsPanel = () => {
id="display-timezone"
label="Display timezone"
value={localData.timezone}
- onChange={(e) => {
- dispatch(setTimezone({ timezone: e.target.value }));
- }}
+ onChange={handleChange}
+ onBlur={handleBlur}
items={timezones}
+ error={errors["display-timezone"]}
/>
+
+ {progress.isLoading || progress.value !== 0 || errors["logo"] ? (
+ }
+ label={logo?.name}
+ size={logo?.size}
+ progress={progress.value}
+ onClick={removeLogo}
+ error={errors["logo"]}
+ />
+ ) : (
+ ""
+ )}
+
+
+
+