From 5ca6850b4ad14cd2f2fd4577df88a4c66b856123 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 11 Jul 2024 16:45:29 -0400 Subject: [PATCH 1/9] Refactored alert component --- Client/src/Components/Alert/index.css | 8 ++--- Client/src/Components/Alert/index.jsx | 52 +++++++++++++++++++++++++-- Client/src/Pages/Demo/Demo.jsx | 29 +++++++++++++-- 3 files changed, 79 insertions(+), 10 deletions(-) diff --git a/Client/src/Components/Alert/index.css b/Client/src/Components/Alert/index.css index 9f0c397e3..c65bfa38c 100644 --- a/Client/src/Components/Alert/index.css +++ b/Client/src/Components/Alert/index.css @@ -1,9 +1,7 @@ .alert { margin: 0; width: fit-content; - padding: var(--env-var-spacing-1-plus); +} +.alert, .alert button{ font-size: var(--env-var-font-size-medium); -} -.alert .MuiStack-root { - max-width: 85%; -} +} \ No newline at end of file diff --git a/Client/src/Components/Alert/index.jsx b/Client/src/Components/Alert/index.jsx index bf2531bba..cacd0f058 100644 --- a/Client/src/Components/Alert/index.jsx +++ b/Client/src/Components/Alert/index.jsx @@ -1,10 +1,12 @@ import PropTypes from "prop-types"; import { useTheme } from "@emotion/react"; -import { Box, Stack } from "@mui/material"; +import { Box, IconButton, Stack } from "@mui/material"; import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; import ErrorOutlineOutlinedIcon from "@mui/icons-material/ErrorOutlineOutlined"; import WarningAmberOutlinedIcon from "@mui/icons-material/WarningAmberOutlined"; +import CloseIcon from "@mui/icons-material/Close"; import "./index.css"; +import Button from "../Button"; /** * Icons mapping for different alert variants. @@ -25,7 +27,7 @@ const icons = { * @returns {JSX.Element} */ -const Alert = ({ variant, title, body }) => { +const Alert = ({ variant, title, body, toast, hasIcon = true, onClick }) => { const theme = useTheme(); const { bg, border, color } = theme.alert[variant]; const icon = icons[variant]; @@ -34,19 +36,63 @@ const Alert = ({ variant, title, body }) => { - {icon && {icon}} + {hasIcon && {icon}} {title && {title}} {body && {body}} + {hasIcon && toast && ( + + )} + {toast && ( + + + + )} ); }; diff --git a/Client/src/Pages/Demo/Demo.jsx b/Client/src/Pages/Demo/Demo.jsx index 42e30b6d2..5163b51be 100644 --- a/Client/src/Pages/Demo/Demo.jsx +++ b/Client/src/Pages/Demo/Demo.jsx @@ -421,11 +421,20 @@ const Demo = () => { - + console.log("changed")} + /> - } label="image.jpg" size="2 MB" /> + } + label="image.jpg" + size="2 MB" + progress={50} + onClick={() => console.log("click")} + /> @@ -449,6 +458,22 @@ const Demo = () => { body="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." /> + + + + + + From e9cb0aa339f691410b7c5b239839a40799953768 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 11 Jul 2024 16:54:25 -0400 Subject: [PATCH 2/9] Removed ripple effect from icon button and renamed bools --- Client/src/Components/Alert/index.jsx | 10 +++++++--- Client/src/Pages/Demo/Demo.jsx | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Client/src/Components/Alert/index.jsx b/Client/src/Components/Alert/index.jsx index cacd0f058..7cdc972a5 100644 --- a/Client/src/Components/Alert/index.jsx +++ b/Client/src/Components/Alert/index.jsx @@ -27,7 +27,7 @@ const icons = { * @returns {JSX.Element} */ -const Alert = ({ variant, title, body, toast, hasIcon = true, onClick }) => { +const Alert = ({ variant, title, body, isToast, hasIcon = true, onClick }) => { const theme = useTheme(); const { bg, border, color } = theme.alert[variant]; const icon = icons[variant]; @@ -50,7 +50,7 @@ const Alert = ({ variant, title, body, toast, hasIcon = true, onClick }) => { {title && {title}} {body && {body}} - {hasIcon && toast && ( + {hasIcon && isToast && ( )} - {toast && ( + {isToast && ( { "&:focus": { outline: "none", }, + "& .MuiTouchRipple-root": { + pointerEvents: "none", + display: "none", + }, }} > { From 92d4caa801cc15dfb4e1f3492817ae3063ae3ebf Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 11 Jul 2024 18:05:41 -0400 Subject: [PATCH 3/9] Added create toast function --- Client/src/Pages/Demo/Demo.jsx | 18 +++++++++++++++++- Client/src/Utils/Theme.js | 6 +++--- Client/src/Utils/toastUtils.jsx | 26 ++++++++++++++++++++++++++ Client/src/index.css | 9 +++++++++ 4 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 Client/src/Utils/toastUtils.jsx diff --git a/Client/src/Pages/Demo/Demo.jsx b/Client/src/Pages/Demo/Demo.jsx index c5f83d2a8..6a7a07e11 100644 --- a/Client/src/Pages/Demo/Demo.jsx +++ b/Client/src/Pages/Demo/Demo.jsx @@ -40,6 +40,8 @@ import { getMonitorsByUserId } from "../../Features/Monitors/monitorsSlice"; import ImageField from "../../Components/TextFields/Image"; import ProgressUpload from "../../Components/ProgressBars"; import Alert from "../../Components/Alert"; +import { createToast } from "../../Utils/toastUtils"; +import { ToastContainer } from "react-toastify"; const cols = [ { @@ -463,7 +465,7 @@ const Demo = () => { variant="info" title="We've just released a new feature" body="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aliquid pariatur, ipsum dolor." - toast={true} + isToast={true} /> @@ -474,8 +476,22 @@ const Demo = () => { hasIcon={false} /> + + - - -
- -
-
{header}
-
- - -
-
- - ); -}; - -AnnouncementUpdateSubscription.propTypes = { - title: PropTypes.string.isRequired, - text: PropTypes.string.isRequired, - cancel: PropTypes.string.isRequired, - positive: PropTypes.string.isRequired, - header: PropTypes.string.isRequired, - button: PropTypes.string.isRequired, -}; - -export default AnnouncementUpdateSubscription; diff --git a/Client/src/Components/Toast/AnnouncementUpdateSubscription/announcementUpdateSubscription.css b/Client/src/Components/Toast/AnnouncementUpdateSubscription/announcementUpdateSubscription.css deleted file mode 100644 index 6064a86cd..000000000 --- a/Client/src/Components/Toast/AnnouncementUpdateSubscription/announcementUpdateSubscription.css +++ /dev/null @@ -1,86 +0,0 @@ -.update-subscription { - margin: 20px; -} - -.update-subscription, -.update-subscription-dialog { - max-width: 770px; - border-radius: 4px; - border: 1px solid #eaecf0; -} - -.update-subscription-dialog { - padding: 15px 30px; -} - -.subscription-dialog-title { - font-size: 1rem; - font-weight: 700; - color: var(--env-var-color-1); -} - -.v-spacing-small { - height: 10px; -} - -.v-spacing-medium { - height: 15px; -} - -.subscription-dialog-text { - font-size: 13px; - color: var(--env-var-color-2); -} - -.subscription-dialog-controls { - display: flex; -} - -.controls-negative { - padding: 12px 16px; - margin-right: 12px; - border-radius: var(--env-var-radius-1); - border: 1px solid var(--env-var-color-6); - background-color: var(--env-var-color-8); - font-size: var(--env-var-font-size-medium); -} - -.controls-positive, -.subscribe-button { - padding: 12px 16px; - margin-right: 12px; - border-radius: var(--env-var-radius-1); - border: 1px solid var(--env-var-color-7); - background-color: var(--env-var-color-3); - color: var(--env-var-color-8); - font-size: var(--env-var-font-size-medium); -} - -.update-subscription-title { - color: var(--env-var-color-5); - padding: 0 20px; - font-weight: 700; - font-size: 13px; - margin-bottom: 10px; -} - -.subscription-email { - display: flex; - padding: 0 20px; -} - -.subscription-email-field { - width: 290px; - height: 24px; - border-radius: var(--env-var-radius-1); - border: 1px solid var(--env-var-color-4); - box-shadow: 0px 1px 2px 0px rgba(101, 182, 28, 0.05); - padding: 5px 15px; - margin-right: 20px; -} - -.subscribe-button { - width: 100px; - height: 34px; - padding: 2px 15px; -} diff --git a/Client/src/Components/Toast/AnnouncementsDualButton/AnnouncementsDualButton.jsx b/Client/src/Components/Toast/AnnouncementsDualButton/AnnouncementsDualButton.jsx deleted file mode 100644 index 2abbadb1e..000000000 --- a/Client/src/Components/Toast/AnnouncementsDualButton/AnnouncementsDualButton.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import "./announcementsDualButton.css"; -import React from "react"; -import CloseIcon from "@mui/icons-material/Close"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material"; - -/** - * @component - * @param {Object} props - * @param {string} props.subject - The announcement subject text - * @param {string} props.body - The announcement body text content - * @param {string} props.esc - The text for the escape button - * @param {string} props.primary - The text for the primary button - * @returns {JSX.Element} - Renders the announcement dual button component - */ - -const AnnouncementsDualButton = ({ subject, body, esc, primary }) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
-
-
{subject}
-
-
{body}
-
-
{esc}
-
{primary}
-
-
-
-
- -
-
- ); -}; - -AnnouncementsDualButton.propTypes = { - subject: PropTypes.string.isRequired, - body: PropTypes.string.isRequired, - esc: PropTypes.string.isRequired, - primary: PropTypes.string.isRequired, -}; - -export default AnnouncementsDualButton; diff --git a/Client/src/Components/Toast/AnnouncementsDualButton/announcementsDualButton.css b/Client/src/Components/Toast/AnnouncementsDualButton/announcementsDualButton.css deleted file mode 100644 index aff02ab46..000000000 --- a/Client/src/Components/Toast/AnnouncementsDualButton/announcementsDualButton.css +++ /dev/null @@ -1,25 +0,0 @@ -.announcement-without-tile { - margin: 20px; - max-width: 343px; - width: fit-content; - display: flex; - border: 1px solid var(--env-var-color-4); - padding: 16px; - border-radius: var(--env-var-radius-1); -} - -.h-spacing { - width: 20px; -} - -.announcement-without-content-subject { - font-size: var(--env-var-font-size-medium); - color: var(--env-var-color-5); - font-weight: 700; - margin-bottom: 10px; -} - -.announcement-without-content-body { - font-size: var(--env-var-font-size-medium); - color: var(--env-var-color-2); -} diff --git a/Client/src/Components/Toast/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon.jsx b/Client/src/Components/Toast/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon.jsx deleted file mode 100644 index bd2675f4c..000000000 --- a/Client/src/Components/Toast/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import "./announcementsDualButtonWithIcon.css"; -import CloseIcon from "@mui/icons-material/Close"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material"; - -/** - * @component - * @param {Object} props - * @param {React.ReactNode} props.icon - Optional React node for an announcement icon - * @param {string} props.subject - The announcement subject text - * @param {string} props.body - The announcement body text content - * @param {string} props.esc - The text for the escape button (usually "Close") - * @param {string} props.primary - The text for the primary button - * @param {function} props.closeToast - Function to close the toast notification - * @returns {JSX.Element} - Renders the announcement dual button with icon component - */ - -const AnnouncementsDualButtonWithIcon = ({ - icon, - subject, - body, - esc, - primary, - closeToast, -}) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
- {icon &&
{icon}
} -
- {subject && ( -
{subject}
- )} - {body &&
{body}
} - {(esc || primary) && ( -
- {esc && ( -
- {esc} -
- )} - {primary && ( -
{primary}
- )} -
- )} -
-
- -
-
- ); -}; - -AnnouncementsDualButtonWithIcon.propTypes = { - icon: PropTypes.object, - subject: PropTypes.string, - body: PropTypes.string, - esc: PropTypes.string, - primary: PropTypes.string, - closeToast: PropTypes.func.isRequired, -}; - -export default AnnouncementsDualButtonWithIcon; diff --git a/Client/src/Components/Toast/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css b/Client/src/Components/Toast/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css deleted file mode 100644 index 02f132374..000000000 --- a/Client/src/Components/Toast/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css +++ /dev/null @@ -1,58 +0,0 @@ -.announcement-tile { - margin: 20px; - width: fit-content; - display: flex; - border: 1px solid var(--env-var-color-4); - padding: 16px; - border-radius: var(--env-var-radius-1); -} - -.info-icon-frame { - display: flex; - width: var(--env-var-default-2); - height: var(--env-var-default-2); - border: 1px solid var(--env-var-color-6); - border-radius: var(--env-var-radius-2); - justify-content: center; - align-items: center; -} - -.announcement-content { - padding: 0 16px; -} - -.announcement-content-subject { - font-size: var(--env-var-font-size-medium); - color: var(--env-var-color-5); - font-weight: 700; - margin-bottom: 10px; -} - -.announcement-content-body { - font-size: var(--env-var-font-size-medium); - color: var(--env-var-color-2); - font-weight: 300; -} - -.announcement-content-controllers { - display: flex; - font-size: var(--env-var-font-size-medium); - margin-top: 15px; -} - -.controllers-button-esc { - font-size: var(--env-var-font-size-medium); - font-weight: 600; - margin-right: 12px; -} - -.controllers-button-primary { - font-size: var(--env-var-font-size-medium); - font-weight: 600; - color: var(--env-var-color-3); -} - -.announcement-close { - width: var(--env-var-default-1); - height: var(--env-var-default-1); -} diff --git a/Client/src/Components/Toast/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx b/Client/src/Components/Toast/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx deleted file mode 100644 index 20a179052..000000000 --- a/Client/src/Components/Toast/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import "./announcementsMessageBar.css"; -import React from "react"; -import CloseIcon from "@mui/icons-material/Close"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material"; - -/** - * @component - * @param {Object} props - * @param {string} props.message - The announcement message text content (required) - * @returns {JSX.Element} - Renders the announcement message bar component - */ -const AnnouncementsMessageBar = ({ message }) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
-
{message}
-
- -
-
- ); -}; - -AnnouncementsMessageBar.propTypes = { - message: PropTypes.string.isRequired, -}; - -export default AnnouncementsMessageBar; diff --git a/Client/src/Components/Toast/AnnouncementsMessageBar/announcementsMessageBar.css b/Client/src/Components/Toast/AnnouncementsMessageBar/announcementsMessageBar.css deleted file mode 100644 index 7a6e30b0a..000000000 --- a/Client/src/Components/Toast/AnnouncementsMessageBar/announcementsMessageBar.css +++ /dev/null @@ -1,8 +0,0 @@ -.announcement-messagebar-body { - margin: 20px; - font-size: var(--env-var-font-size-medium); - padding: 0 16px 0 0; - justify-content: center; - align-items: center; - margin: auto; -} diff --git a/Client/src/Components/Toast/index.css b/Client/src/Components/Toast/index.css deleted file mode 100644 index c778d22f5..000000000 --- a/Client/src/Components/Toast/index.css +++ /dev/null @@ -1,3 +0,0 @@ -.Toastify__toast-container { - width: auto; -} \ No newline at end of file diff --git a/Client/src/Components/Toast/index.jsx b/Client/src/Components/Toast/index.jsx deleted file mode 100644 index 41367ec11..000000000 --- a/Client/src/Components/Toast/index.jsx +++ /dev/null @@ -1,52 +0,0 @@ -import PropTypes from "prop-types"; -import ComplexAlert from "../../Components/Icons/ComplexAlert/ComplexAlert"; -import AnnouncementsDualButtonWithIcon from "./AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon"; -import "react-toastify/dist/ReactToastify.css"; -import { toast, ToastContainer } from "react-toastify"; -import "./index.css"; - -/** - * ToastComponent displays a notification that is triggered by error messages. - * - * @component - * @param {Object} props - The component props - * @param {string} props.subject - The subject or title of the toast message - * @param {string} props.body - The body content of the toast message - * @param {string} props.esc - The text for the dismiss action button - * @param {string} props.primary - The text for the primary action button - * @returns {JSX.Element} JSX element representing the ToastComponent - */ - -const ToastComponent = ({ subject, body, esc, primary }) => { - const displayMsg = () => { - toast( - ({ closeToast, toastProps }) => ( - } - subject={subject} - body={body} - esc={esc} - primary={primary} - closeToast={closeToast} - /> - ), - { closeButton: false } - ); - }; - - return ( -
- - -
- ); -}; - -ToastComponent.propTypes = { - subject: PropTypes.string.isRequired, - body: PropTypes.string.isRequired, - esc: PropTypes.string.isRequired, - primary: PropTypes.string.isRequired, -}; - -export default ToastComponent; diff --git a/Client/src/Pages/PlayGround/PlayGround.jsx b/Client/src/Pages/PlayGround/PlayGround.jsx index bde0b6761..247146362 100644 --- a/Client/src/Pages/PlayGround/PlayGround.jsx +++ b/Client/src/Pages/PlayGround/PlayGround.jsx @@ -4,16 +4,9 @@ import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline"; import HelpOutlineIcon from "@mui/icons-material/HelpOutline"; import WebsiteTextField from "../../Components/TextFields/Website/WebsiteTextField"; import DescriptionTextField from "../../Components/TextFields/Description/DescriptionTextField"; -import AnnouncementsDualButton from "../../Components/Toast/AnnouncementsDualButton/AnnouncementsDualButton"; -import AnnouncementsDualButtonWithIcon from "../../Components/Toast/AnnouncementsDualButtonWithIcon/AnnouncementsDualButtonWithIcon"; -import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; -import ComplexAlert from "../../Components/Icons/ComplexAlert/ComplexAlert"; -import AnnouncementsMessageBar from "../../Components/Toast/AnnouncementsMessageBar/AnnouncementsMessageBar"; -import AnnouncementUpdateSubscription from "../../Components/Toast/AnnouncementUpdateSubscription/AnnouncementUpdateSubscription"; import PlayGroundCharts from "./PlayGround-Charts"; import PlayGroundPopupModals from "./PlayGround-Popup-Modals"; import PlayGroundTooltips from "./PlayGround-Tooltips"; -import Toast from "../../Components/Toast"; // This Component is just for the development and test // purposes and just to see what my components look like while development @@ -53,7 +46,6 @@ function PlayGround() { hasCopyButton={true} hintText="This is a hint text to help user." /> -
{/* Now, illustration of the Description text fields */} @@ -66,55 +58,6 @@ function PlayGround() { hasError={true} hintText="This is a hint text to help user." /> - - - - } - subject="We’ve just released a new feature" - body="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aliquid - pariatur, ipsum dolor." - esc="Dismiss" - primary="View changes" - /> - - } - subject="There was a problem with that action" - body="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aliquid - pariatur, ipsum dolor." - esc="Dismiss" - primary="Learn more" - /> - - } - subject="Successfully updated profile" - body="Lorem ipsum dolor sit amet consectetur adipisicing elit. Aliquid - pariatur, ipsum dolor." - esc="Dismiss" - primary="Learn more" - /> - - - - - -

From 7e1ccff00df698b4187e444c5a60f05095193446 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 11 Jul 2024 18:53:14 -0400 Subject: [PATCH 5/9] Added toast alerts to profile panel --- .../TabPanels/Account/ProfilePanel.jsx | 30 +++++++++++++------ Client/src/Pages/Account/index.jsx | 3 ++ Client/src/Utils/toastUtils.jsx | 2 +- Client/src/index.css | 3 ++ 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx index ab44b34b3..2fdbba2e6 100644 --- a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx +++ b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx @@ -22,7 +22,7 @@ import ImageIcon from "@mui/icons-material/Image"; import ProgressUpload from "../../ProgressBars"; import { formatBytes } from "../../../Utils/fileUtils"; import { clearMonitorState } from "../../../Features/Monitors/monitorsSlice"; - +import { createToast } from "../../../Utils/toastUtils"; /** * ProfilePanel component displays a form for editing user profile information * and allows for actions like updating profile picture, credentials, @@ -153,12 +153,8 @@ const ProfilePanel = () => { setIsOpen(""); }; - console.log( - localData.deleteProfileImage === false && localData.file === undefined - ); - // Handles form submission to update user profile - const handleSaveProfile = (event) => { + const handleSaveProfile = async (event) => { event.preventDefault(); if ( localData.firstname === user.firstname && @@ -166,12 +162,28 @@ const ProfilePanel = () => { localData.deleteProfileImage === undefined && localData.file === undefined ) { - // TODO: Add toast/notification for unchanged profile data + createToast({ + variant: "error", + body: "Unable to update profile: No changes detected.", + hasIcon: false, + }); return; } - dispatch(update({ authToken, localData })); - // TODO: Add toast/notification for profile update success + const action = await dispatch(update({ authToken, localData })); + if (action.payload.success) { + createToast({ + variant: "info", + body: "Your profile data was changed successfully.", + hasIcon: false, + }); + } else { + createToast({ + variant: "error", + body: "There was an error updating your profile data.", + hasIcon: false, + }); + } }; // Removes current profile image from UI diff --git a/Client/src/Pages/Account/index.jsx b/Client/src/Pages/Account/index.jsx index 2bb4dc018..ec010f006 100644 --- a/Client/src/Pages/Account/index.jsx +++ b/Client/src/Pages/Account/index.jsx @@ -8,6 +8,8 @@ import ProfilePanel from "../../Components/TabPanels/Account/ProfilePanel"; import PasswordPanel from "../../Components/TabPanels/Account/PasswordPanel"; import TeamPanel from "../../Components/TabPanels/Account/TeamPanel"; import { useNavigate } from "react-router"; +import "react-toastify/dist/ReactToastify.css"; +import { ToastContainer } from "react-toastify"; /** * Account component renders a settings page with tabs for Profile, Password, and Team settings. @@ -73,6 +75,7 @@ const Account = ({ open = "profile" }) => { +
); }; diff --git a/Client/src/Utils/toastUtils.jsx b/Client/src/Utils/toastUtils.jsx index 01beaeaea..7fb29ff6d 100644 --- a/Client/src/Utils/toastUtils.jsx +++ b/Client/src/Utils/toastUtils.jsx @@ -3,7 +3,7 @@ import Alert from "../Components/Alert"; export const createToast = ({ variant, title, body, hasIcon, config = {} }) => { const toastConfig = { - position: "top-right", + position: "bottom-right", autoClose: 3000, hideProgressBar: true, closeButton: false, diff --git a/Client/src/index.css b/Client/src/index.css index 1ba3b2239..086692678 100644 --- a/Client/src/index.css +++ b/Client/src/index.css @@ -124,6 +124,9 @@ button:focus-visible { outline: 4px auto -webkit-focus-ring-color; } +.Toastify__toast-container { + width: auto; +} .Toastify [class^="Toastify__toast"] { padding: 0; margin: 0; From e9bab07309fcdcaa8d256aebc1fd791b77ecbb0d Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 11 Jul 2024 18:55:26 -0400 Subject: [PATCH 6/9] Added small fix to prevent spamming the save button --- Client/src/Components/TabPanels/Account/ProfilePanel.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx index 2fdbba2e6..27949be8a 100644 --- a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx +++ b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx @@ -58,6 +58,7 @@ const ProfilePanel = () => { // Handles input field changes and performs validation const handleChange = (event) => { + errors["unchanged"] && clearError("unchanged"); const { value, id } = event.target; const name = idToName[id]; setLocalData((prev) => ({ @@ -163,10 +164,11 @@ const ProfilePanel = () => { localData.file === undefined ) { createToast({ - variant: "error", + variant: "warning", body: "Unable to update profile: No changes detected.", hasIcon: false, }); + setErrors({ unchanged: "unable to update profile" }); return; } From 8bd8c292b8e549b178b912071464d61a9fc05825 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 11 Jul 2024 19:00:31 -0400 Subject: [PATCH 7/9] Added toast alerts to password panel --- .../Components/TabPanels/Account/PasswordPanel.jsx | 13 +++++++++++-- .../Components/TabPanels/Account/ProfilePanel.jsx | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Client/src/Components/TabPanels/Account/PasswordPanel.jsx b/Client/src/Components/TabPanels/Account/PasswordPanel.jsx index d0f6e6f62..0e035f5cf 100644 --- a/Client/src/Components/TabPanels/Account/PasswordPanel.jsx +++ b/Client/src/Components/TabPanels/Account/PasswordPanel.jsx @@ -8,6 +8,7 @@ import { editPasswordValidation } from "../../../Validation/validation"; import Alert from "../../Alert"; import { update } from "../../../Features/Auth/authSlice"; import { useDispatch, useSelector } from "react-redux"; +import { createToast } from "../../../Utils/toastUtils"; /** * PasswordPanel component manages the form for editing password. @@ -78,15 +79,23 @@ const PasswordPanel = () => { } else { const action = await dispatch(update({ authToken, localData })); if (action.payload.success) { - // TODO: Add toast/notification for password update success + createToast({ + variant: "info", + body: "Your password was changed successfully.", + hasIcon: false, + }); setLocalData({ password: "", newPassword: "", confirm: "", }); } else { - // TODO: Add toast/notification for password update rejected // TODO: Check for other errors? + createToast({ + variant: "info", + body: "Your password input was incorrect.", + hasIcon: false, + }); setErrors({ password: "*" + action.payload.msg + "." }); } } diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx index 27949be8a..52d10a2f5 100644 --- a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx +++ b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx @@ -86,7 +86,7 @@ const ProfilePanel = () => { delete: false, }); - //TODO - potentitally remove once image compression functions are implemented + //TODO - potentitally remove, will revisit in the future intervalRef.current = setInterval(() => { const buffer = 12; setProgress((prev) => { From 91abe374f6e50fdf952f3c2dac4ccbafb64ee070 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 11 Jul 2024 20:30:11 -0400 Subject: [PATCH 8/9] Added jsdocs and proptypes --- Client/src/Components/Alert/index.jsx | 13 +++++++++++++ Client/src/Utils/toastUtils.jsx | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/Client/src/Components/Alert/index.jsx b/Client/src/Components/Alert/index.jsx index 7cdc972a5..3fa0d82ab 100644 --- a/Client/src/Components/Alert/index.jsx +++ b/Client/src/Components/Alert/index.jsx @@ -24,6 +24,9 @@ const icons = { * @param {'info' | 'error' | 'warning'} props.variant - The type of alert. * @param {string} [props.title] - The title of the alert. * @param {string} [props.body] - The body text of the alert. + * @param {boolean} [props.isToast] - Indicates if the alert is used as a toast notification. + * @param {boolean} [props.hasIcon] - Whether to display an icon in the alert. + * @param {function} props.onClick - Toast dismiss function. * @returns {JSX.Element} */ @@ -105,6 +108,16 @@ Alert.propTypes = { variant: PropTypes.oneOf(["info", "error", "warning"]).isRequired, title: PropTypes.string, body: PropTypes.string, + isToast: PropTypes.bool, + hasIcon: PropTypes.bool, + onClick: function (props, propName, componentName) { + if (props.isToast && !props[propName]) { + return new Error( + `Prop '${propName}' is required when 'isToast' is true in '${componentName}'.` + ); + } + return null; + }, }; export default Alert; diff --git a/Client/src/Utils/toastUtils.jsx b/Client/src/Utils/toastUtils.jsx index 7fb29ff6d..73c21c260 100644 --- a/Client/src/Utils/toastUtils.jsx +++ b/Client/src/Utils/toastUtils.jsx @@ -1,6 +1,16 @@ +import PropTypes from "prop-types"; import { toast } from "react-toastify"; import Alert from "../Components/Alert"; +/** + * @param {object} props + * @param {'info' | 'error' | 'warning'} - The variant of the alert (e.g., "info", "error"). + * @param {string} props.title - The title of the alert. + * @param {string} props.body - The body/content of the alert. + * @param {boolean} props.hasIcon - Whether the alert should include an icon. + * @param {object} [props.config] - Additional configuration props for the toast. + */ + export const createToast = ({ variant, title, body, hasIcon, config = {} }) => { const toastConfig = { position: "bottom-right", @@ -24,3 +34,11 @@ export const createToast = ({ variant, title, body, hasIcon, config = {} }) => { toastConfig ); }; + +createToast.propTypes = { + variant: PropTypes.oneOf(["info", "error", "warning"]).isRequired, + title: PropTypes.string, + body: PropTypes.string, + hasIcon: PropTypes.bool, + config: PropTypes.object, +}; From e2b9a6fa161a84cd75487ed81c07fc35f91a07ce Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Fri, 12 Jul 2024 11:33:34 -0400 Subject: [PATCH 9/9] Clear unchanged error when picture is modified --- Client/src/Components/TabPanels/Account/ProfilePanel.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx index 52d10a2f5..e96f507d9 100644 --- a/Client/src/Components/TabPanels/Account/ProfilePanel.jsx +++ b/Client/src/Components/TabPanels/Account/ProfilePanel.jsx @@ -23,6 +23,7 @@ import ProgressUpload from "../../ProgressBars"; import { formatBytes } from "../../../Utils/fileUtils"; import { clearMonitorState } from "../../../Features/Monitors/monitorsSlice"; import { createToast } from "../../../Utils/toastUtils"; + /** * ProfilePanel component displays a form for editing user profile information * and allows for actions like updating profile picture, credentials, @@ -152,6 +153,7 @@ const ProfilePanel = () => { deleteProfileImage: false, })); setIsOpen(""); + errors["unchanged"] && clearError("unchanged"); }; // Handles form submission to update user profile @@ -194,6 +196,7 @@ const ProfilePanel = () => { ...prev, deleteProfileImage: true, })); + errors["unchanged"] && clearError("unchanged"); }; // Initiates the account deletion process