Repository: alvarotorrestx/DevConnect
Pull request #84: Implement the newly created Error/Success Toast to EditProfile #82
Author: login: Nishantsingh11
Created: 2025-04-10T06:40:27Z | Merged: 2025-04-12T15:01:01Z

# Implement the newly created Error/Success Toast to EditProfile #82


## Related issue #82 


### Screenshot/video
[Screencast From 2025-04-10 12-05-13.webm](https://github.com/user-attachments/assets/b81256e5-3a7c-412f-849a-7f5ee7a7b1c6)



Diff:
diff --git a/client/src/assets/components/profile/EditProfile.jsx b/client/src/assets/components/profile/EditProfile.jsx
index 0f5b72c6..b6be3172 100644
--- a/client/src/assets/components/profile/EditProfile.jsx
+++ b/client/src/assets/components/profile/EditProfile.jsx
@@ -7,12 +7,34 @@ import { axiosPrivate } from '../../../api/axios';
 import useAuth from '../../../auth/useAuth';
 import { useNavigate } from 'react-router-dom';
 import { FaUserCheck } from "react-icons/fa";
+import { FaTimesCircle } from "react-icons/fa";
+import { FaCheckCircle } from "react-icons/fa";
+
+// Toast imports
+import ErrorToast from "../toast/ErrorToast";
+import { useErrorToast } from "../toast/useErrorToast";
+import SuccessToast from "../toast/SuccessToast";
+import { useSuccessToast } from "../toast/useSuccessToast";
+
+
 
 const EditProfile = () => {
   const { profile, loading } = useContext(ProfileContext);
 
   const [formData, setFormData] = useState(null);
 
+  const {
+    message: errorMessage,
+    show: showErrorToast,
+    showError,
+  } = useErrorToast();
+
+  const {
+    message: successMessage,
+    show: showSuccessToast,
+    showSuccess,
+  } = useSuccessToast();
+
   useEffect(() => {
     if (profile) {
       setFormData({
@@ -68,7 +90,6 @@ const EditProfile = () => {
     inputRefs[field]?.current?.focus();
   };
 
-
   const handleChange = (e) => {
     setFormData(prev => ({ ...prev, [e.target.id]: e.target.value }));
   };
@@ -78,7 +99,6 @@ const EditProfile = () => {
     handleIsEditing(field);
   };
 
-
   const { auth } = useAuth();
 
   const isOwnProfile = profile?.username === auth?.username;
@@ -92,13 +112,6 @@ const EditProfile = () => {
     }
   }, [loading, profile, allowEditing, navigate]);
 
-  const errRef = useRef();
-  const [errMsg, setErrMsg] = useState('');
-
-  useEffect(() => {
-    const hasEdits = Object.values(isEditing).some(Boolean);
-    if (hasEdits) setErrMsg('');
-  }, [formData, isEditing]);
 
   const handleSave = async (field) => {
 
@@ -116,14 +129,12 @@ const EditProfile = () => {
 
     const username = formData.username?.trim();
     if (!username) {
-      setErrMsg("Username is required.");
-      errRef.current.focus();
+      showError("Username is required.");
       return;
     }
 
     if (field === 'username' && !USERNAME_REGEX.test(formData.username)) {
-      setErrMsg("Invalid username.\n5 to 30 characters.\nLetters, numbers, and hyphens (-) allowed.\nLowercase only.");
-      errRef.current.focus();
+      showError("Invalid username. 5 to 30 characters. Letters, numbers, and hyphens (-) allowed. Lowercase only.");
       return;
     }
 
@@ -136,16 +147,20 @@ const EditProfile = () => {
           headers: { Authorization: `Bearer ${auth?.accessToken}` },
           withCredentials: true
         })
+      if (response?.status === 200) {
+        showSuccess(" Profile updated successfully");
+      }
+      else {
+        showError("Update failed");
+      }
     }
     catch (err) {
       // If no error response
       if (!err?.response) {
-        setErrMsg('No Server Response');
+        showError('No Server Response');
       } else {
-        setErrMsg(`${JSON.stringify(err.response.data.message).slice(1, -1)}` || 'Update Failed');
+        showError(`${JSON.stringify(err.response.data.message).slice(1, -1)}` || 'Update Failed');
       }
-
-      errRef.current.focus();
     }
     finally {
       handleIsEditing(field);
@@ -190,14 +205,6 @@ const EditProfile = () => {
         <FaUserCheck />
       </a>
 
-      <div className={errMsg ? "alert alert-error animate-fade flex mt-6" : "offscreen"} tabIndex="-1" ref={errRef} aria-live='assertive'>
-        <svg xmlns="http://www.w3.org/2000/svg" className="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
-        <span>
-          {errMsg.replaceAll('\\n', '\n').split('\n').map((line, i) => (
-            <p key={i}>{line}</p>
-          ))}
-        </span>
-      </div>
 
       {/* First Name Field */}
       <div className="form-control mt-6">
@@ -700,9 +707,25 @@ const EditProfile = () => {
         )}
       </div>
       {/* End Skills Field */}
-
-    </div>
+      {/* Add Toast Components */}
+      <SuccessToast
+        message={successMessage}
+        show={showSuccessToast}
+        status="success"
+        icon={
+          <FaCheckCircle className="text-green-600 text-4xl bg-transparent p-0 m-0" />
+        }
+        iconBgColor="bg-blue-200"
+      />
+      <ErrorToast
+        message={errorMessage}
+        show={showErrorToast}
+        status="error"
+        icon={<FaTimesCircle className="text-red-600 text-4xl" />}
+        iconBgColor="bg-red-700"
+      />
+    </div >
   );
 };
 
-export default EditProfile;
+export default EditProfile;
\ No newline at end of file
diff --git a/client/src/assets/components/toast/ErrorToast.jsx b/client/src/assets/components/toast/ErrorToast.jsx
index 3a23b04a..17050214 100644
--- a/client/src/assets/components/toast/ErrorToast.jsx
+++ b/client/src/assets/components/toast/ErrorToast.jsx
@@ -15,12 +15,12 @@ const ErrorToast = ({ message, show, icon, iconBgColor, status, onClose }) => {
           <Alert className="alert alert-error shadow-lg relative pr-2 rounded-[8px]">
 
             {icon && (
-             <div className="rounded-full bg-white">
+              <div className="rounded-full bg-white">
                 {icon}
               </div>
-              
+
             )}
-<span className="font-medium text-[19px]">
+            <span className="font-medium text-[19px]">
               {message}
             </span>
           </Alert>


Reviews:
user: login: alvarotorrestx
body: Overall, nice work. I thank you for contributing to this project! 😀

There are some updates that need to be implemented to make this appropriate but please don't hesitate to respond with questions and/or feedback of your own!

The errMsg should be replace with showError to get rid of the old div holding the error message.

Furthermore, the toast popup seems to be misaligned and not properly centered.

Please test out Login.jsx or Register.jsx to see the differences.

Again, you can reference: https://github.com/alvarotorrestx/DevConnect/blob/dev/client/src/assets/components/subcomponents/Login.jsx

Can't wait to see what you come up with, you've done a great job so far! :)
state: CHANGES_REQUESTED
author_association: OWNER
submitted_at: 2025-04-10T16:56:45Z
user: login: alvarotorrestx
body: GREAT JOB!!

You updated a lot of the vital information.

Just some lines of code to remove and 1 centering issue to fix and should be good. I'll update screenshot in comments. :)
state: CHANGES_REQUESTED
author_association: OWNER
submitted_at: 2025-04-11T21:54:21Z
user: login: alvarotorrestx
body: Well done! Clean implement! 😁 
state: APPROVED
author_association: OWNER
submitted_at: 2025-04-12T15:00:26Z

Review comments:
diff_hunk: @@ -700,7 +733,15 @@ const EditProfile = () => {
         )}
       </div>
       {/* End Skills Field */}
-
+      {/* Toast components */}
+      <ErrorToast
+        message={errorMessage}
+        show={showErrorToast}
+      />
+      <SuccessToast
+        message={successMessage}
+        show={showSuccessToast}
+      />
path: client/src/assets/components/profile/EditProfile.jsx
user: login: alvarotorrestx
body: This seems to be missing the status, icon and iconBgColor, please reference to how it was originally implemented in Login.jsx for example:
https://github.com/alvarotorrestx/DevConnect/blob/dev/client/src/assets/components/subcomponents/Login.jsx
created_at: 2025-04-10T14:38:33Z
author_association: OWNER
diff_hunk: @@ -136,6 +162,13 @@ const EditProfile = () => {
           headers: { Authorization: `Bearer ${auth?.accessToken}` },
           withCredentials: true
         })
+      if (response?.status === 200) {
+        showSuccess(`${field} updated successfully`);
+        setErrMsg('');
+      }
path: client/src/assets/components/profile/EditProfile.jsx
user: login: alvarotorrestx
body: This is a great addition but {field} might not be too user friendly in the future when we hit production.

Consider using:
```
showSuccess(`Profile updated successfully.`);
```
For the moment while there's a work around for "First Name" updated successfully instead of firstName for instance.

Also since you'll be adding the error toast as well, some logic can be removed through the entire file relating to errMsg, setErrMsg
created_at: 2025-04-10T16:52:47Z
author_association: OWNER
diff_hunk: @@ -136,6 +162,13 @@ const EditProfile = () => {
           headers: { Authorization: `Bearer ${auth?.accessToken}` },
           withCredentials: true
         })
+      if (response?.status === 200) {
+        showSuccess(`${field} updated successfully`);
+        setErrMsg('');
+      }
+      else {
+        showError(`${field} update failed`);
+      }
path: client/src/assets/components/profile/EditProfile.jsx
user: login: alvarotorrestx
body: The catch error block below this also catches the error and sends the response so this isn't needed.

The catch (err) is overwriting the response causing the error toast not to show.
created_at: 2025-04-10T16:53:34Z
author_association: OWNER
diff_hunk: @@ -7,12 +7,50 @@ import { axiosPrivate } from '../../../api/axios';
 import useAuth from '../../../auth/useAuth';
 import { useNavigate } from 'react-router-dom';
 import { FaUserCheck } from "react-icons/fa";
+import { FaTimesCircle } from "react-icons/fa";
+import { FaCheckCircle } from "react-icons/fa";
+
+// Toast imports
+import ErrorToast from "../toast/ErrorToast";
+import { useErrorToast } from "../toast/useErrorToast";
+import SuccessToast from "../toast/SuccessToast";
+import { useSuccessToast } from "../toast/useSuccessToast";
+
+
+
+// Toast imports
+import ErrorToast from "../toast/ErrorToast";
+import { useErrorToast } from "../toast/useErrorToast";
+import SuccessToast from "../toast/SuccessToast";
+import { useSuccessToast } from "../toast/useSuccessToast";
+
+
path: client/src/assets/components/profile/EditProfile.jsx
user: login: alvarotorrestx
body: This is already declared, it causes a crash in the app due to the double declaration.
created_at: 2025-04-11T19:24:23Z
author_association: OWNER
diff_hunk: @@ -7,12 +7,50 @@ import { axiosPrivate } from '../../../api/axios';
 import useAuth from '../../../auth/useAuth';
 import { useNavigate } from 'react-router-dom';
 import { FaUserCheck } from "react-icons/fa";
+import { FaTimesCircle } from "react-icons/fa";
+import { FaCheckCircle } from "react-icons/fa";
+
+// Toast imports
+import ErrorToast from "../toast/ErrorToast";
+import { useErrorToast } from "../toast/useErrorToast";
+import SuccessToast from "../toast/SuccessToast";
+import { useSuccessToast } from "../toast/useSuccessToast";
+
+
+
+// Toast imports
+import ErrorToast from "../toast/ErrorToast";
+import { useErrorToast } from "../toast/useErrorToast";
+import SuccessToast from "../toast/SuccessToast";
+import { useSuccessToast } from "../toast/useSuccessToast";
+
+
 
 const EditProfile = () => {
   const { profile, loading } = useContext(ProfileContext);
 
   const [formData, setFormData] = useState(null);
 
+<<<<<<< HEAD
+
+
+
+
+=======
+  // Toast hooks
path: client/src/assets/components/profile/EditProfile.jsx
user: login: alvarotorrestx
body: Not sure if this merge issue popped up after you committed, but if it's just GitHub I can fix it. Just wanted your feedback on it.
created_at: 2025-04-11T19:26:25Z
author_association: OWNER
diff_hunk: @@ -92,13 +128,8 @@ const EditProfile = () => {
     }
   }, [loading, profile, allowEditing, navigate]);
 
-  const errRef = useRef();
-  const [errMsg, setErrMsg] = useState('');
-
-  useEffect(() => {
-    const hasEdits = Object.values(isEditing).some(Boolean);
-    if (hasEdits) setErrMsg('');
-  }, [formData, isEditing]);
+  // Remove the error ref and error state
+  // We'll use the toast hooks instead
path: client/src/assets/components/profile/EditProfile.jsx
user: login: alvarotorrestx
body: Comment can be removed
created_at: 2025-04-11T19:27:18Z
author_association: OWNER
diff_hunk: @@ -190,14 +236,7 @@ const EditProfile = () => {
         <FaUserCheck />
       </a>
 
-      <div className={errMsg ? "alert alert-error animate-fade flex mt-6" : "offscreen"} tabIndex="-1" ref={errRef} aria-live='assertive'>
-        <svg xmlns="http://www.w3.org/2000/svg" className="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
-        <span>
-          {errMsg.replaceAll('\\n', '\n').split('\n').map((line, i) => (
-            <p key={i}>{line}</p>
-          ))}
-        </span>
-      </div>
+      {/* Remove the error message div */}
path: client/src/assets/components/profile/EditProfile.jsx
user: login: alvarotorrestx
body: Comment can be removed
created_at: 2025-04-11T20:34:12Z
author_association: OWNER
diff_hunk: @@ -700,9 +739,37 @@ const EditProfile = () => {
         )}
       </div>
       {/* End Skills Field */}
-
+<<<<<<< HEAD
+      {/* Toast components */}
+      <ErrorToast
+        message={errorMessage}
+        show={showErrorToast}
+      />
+      <SuccessToast
+        message={successMessage}
+        show={showSuccessToast}
+=======
path: client/src/assets/components/profile/EditProfile.jsx
user: login: alvarotorrestx
body: Not sure if this merge issue popped up after you committed, but if it's just GitHub I can fix it. Just wanted your feedback on it.
created_at: 2025-04-11T21:46:37Z
author_association: OWNER

PR comments:
user: login: alvarotorrestx
created_at: 2025-04-10T16:57:39Z
body: Hey thanks again @Nishantsingh11 for contributing! I'd love to connect with you to properly get to know you, welcome you to the project and share proper .env credentials.

Please reach out to me on Discord and share your username with me as well if interested to continue contributing! :)

Discord: Simulint
author_association: OWNER
user: login: Nishantsingh11
created_at: 2025-04-11T06:50:51Z
body: Hey @alvarotorrestx ! Thanks a lot for the warm welcome 😊
I’d absolutely love to work with you and keep contributing to the project!
I’ve made the change you requested, and I’ll be reaching out to you on Discord soon.
Also, yes—I'll definitely be working on the other issues as well. Looking forward to collaborating more!
author_association: CONTRIBUTOR
user: login: alvarotorrestx
created_at: 2025-04-11T21:55:55Z
body: Hey @Nishantsingh11 great updates! I added some comments for fixing and after this should be perfect for merging :)

Here are the screenshots for the centering issue:
![image](https://github.com/user-attachments/assets/08adc04f-ded7-4d33-a8df-75ec725e3367)
![image](https://github.com/user-attachments/assets/484dbfb4-c622-46cf-a6e9-1b15f7a54a37)

How it should be:
![image](https://github.com/user-attachments/assets/ca559ecf-d006-468c-9efb-06d718f82343)
![image](https://github.com/user-attachments/assets/fd923356-9aaa-4fad-88b3-90392b13bd99)

author_association: OWNER
user: login: Nishantsingh11
created_at: 2025-04-12T10:01:12Z
body: Hey @alvarotorrestx , I noticed the code to center the toast never got pushed both `login.jsx` and `editProfile.jsx` use the same toast function but the layout update is missing; the cloned repo also doesn’t show any confirmation on successful or failed logins (see the attached video); I’ve resolved the merge conflict locally, so please pull the latest changes and let me know if everything looks good.
sorry my CSS/design skills aren’t the best yet, but let me know if you need anything else!


[Screencast From 2025-04-12 15-30-13.webm](https://github.com/user-attachments/assets/089af6cb-3fda-4767-954f-7659e984a184)
author_association: CONTRIBUTOR
user: login: Nishantsingh11
created_at: 2025-04-12T15:01:34Z
body: @alvarotorrestx thank you 
author_association: CONTRIBUTOR