From b47e17fe91c31ac7810da32b0a83f6868bd3df8e Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 9 Feb 2026 22:07:02 +0000 Subject: [PATCH] cleanup --- client/src/Pages/Auth/CheckEmail.jsx | 248 --------------- .../src/Pages/Auth/NewPasswordConfirmed.jsx | 142 --------- .../Pages/Auth/components/PasswordTooltip.jsx | 116 ------- .../Pages/Auth/hooks/usePasswordFeedback.jsx | 71 ----- .../Pages/Auth/hooks/useValidatePassword.jsx | 70 ----- client/src/Pages/Auth/index.css | 112 ------- client/src/Pages/Auth/old.jsx | 283 ------------------ client/src/Routes/index.jsx | 10 - 8 files changed, 1052 deletions(-) delete mode 100644 client/src/Pages/Auth/CheckEmail.jsx delete mode 100644 client/src/Pages/Auth/NewPasswordConfirmed.jsx delete mode 100644 client/src/Pages/Auth/components/PasswordTooltip.jsx delete mode 100644 client/src/Pages/Auth/hooks/usePasswordFeedback.jsx delete mode 100644 client/src/Pages/Auth/hooks/useValidatePassword.jsx delete mode 100644 client/src/Pages/Auth/index.css delete mode 100644 client/src/Pages/Auth/old.jsx diff --git a/client/src/Pages/Auth/CheckEmail.jsx b/client/src/Pages/Auth/CheckEmail.jsx deleted file mode 100644 index 10cc2ecc6..000000000 --- a/client/src/Pages/Auth/CheckEmail.jsx +++ /dev/null @@ -1,248 +0,0 @@ -import { Box, Button, Stack, Typography } from "@mui/material"; -import { useEffect, useState } from "react"; -import { useTheme } from "@emotion/react"; -import { useDispatch } from "react-redux"; -import { useNavigate, useLocation } from "react-router-dom"; -import { createToast } from "../../Utils/toastUtils.jsx"; -import { forgotPassword } from "../../Features/Auth/authSlice.js"; -import { Trans, useTranslation } from "react-i18next"; -import Background from "@/assets/Images/background-grid.svg?react"; -import Logo from "@/assets/icons/checkmate-icon.svg?react"; -import IconBox from "@/Components/v1/IconBox/index.jsx"; -import Icon from "@/Components/v1/Icon"; -import "./index.css"; - -const CheckEmail = () => { - const theme = useTheme(); - const navigate = useNavigate(); - const dispatch = useDispatch(); - const { t } = useTranslation(); - const location = useLocation(); - - const [email, setEmail] = useState(); - const [disabled, setDisabled] = useState(false); - useEffect(() => { - setEmail(sessionStorage.getItem("email")); - }, []); - - // TODO - fix - const openMail = () => { - window.location.href = "mailto:"; - }; - - const toastFail = [ - { - body: t("auth.forgotPassword.toasts.emailNotFound"), - }, - { - body: t("auth.forgotPassword.toasts.redirect").replace("", "3"), - }, - { - body: t("auth.forgotPassword.toasts.redirect").replace("", "2"), - }, - { - body: t("auth.forgotPassword.toasts.redirect").replace("", "1"), - }, - ]; - - const resendToken = async () => { - setDisabled(true); // prevent resent button from being spammed - if (!email) { - let index = 0; - const interval = setInterval(() => { - if (index < toastFail.length) { - createToast(toastFail[index]); - index++; - } else { - clearInterval(interval); - navigate("/forgot-password"); - } - }, 1000); - } else { - const form = { email: email }; - const action = await dispatch(forgotPassword(form)); - if (action.payload.success) { - createToast({ - body: t("auth.forgotPassword.toasts.sent").replace("", form.email), - }); - setDisabled(false); - } else { - if (action.payload) { - // dispatch errors - createToast({ - body: action.payload.msg, // FIXME: Potential untranslated string - }); - } else { - // unknown errors - createToast({ - body: t("common.toasts.unknownError"), - }); - } - } - } - }; - - const handleNavigate = () => { - sessionStorage.removeItem("email"); - location.state?.from - ? navigate(location.state.from, { replace: true }) - : navigate("/login"); - }; - - return ( - - - - - - - {t("common.appName")} - - .MuiStack-root": { - border: 1, - borderRadius: theme.spacing(5), - borderColor: theme.palette.primary.lowContrast, - backgroundColor: theme.palette.primary.main, - padding: { - xs: theme.spacing(12), - sm: theme.spacing(20), - }, - }, - }} - > - - - - - - - - {t("auth.forgotPassword.heading")} - - - {email || "username@email.com"} - - ), - }} - /> - - - - - - ), - }} - /> - - - - - - Account Password" - : "auth.forgotPassword.links.login" - } - components={{ - a: ( - - ), - }} - /> - - - - ); -}; - -export default CheckEmail; diff --git a/client/src/Pages/Auth/NewPasswordConfirmed.jsx b/client/src/Pages/Auth/NewPasswordConfirmed.jsx deleted file mode 100644 index c92e52552..000000000 --- a/client/src/Pages/Auth/NewPasswordConfirmed.jsx +++ /dev/null @@ -1,142 +0,0 @@ -import { Box, Button, Stack, Typography } from "@mui/material"; -import { useTheme } from "@emotion/react"; -import { useNavigate } from "react-router-dom"; -import { useDispatch } from "react-redux"; -import { clearAuthState } from "../../Features/Auth/authSlice.js"; -import Background from "@/assets/Images/background-grid.svg?react"; -import Logo from "@/assets/icons/checkmate-icon.svg?react"; -import IconBox from "@/Components/v1/IconBox/index.jsx"; -import Icon from "@/Components/v1/Icon"; -import { Trans, useTranslation } from "react-i18next"; -import "./index.css"; - -const NewPasswordConfirmed = () => { - const theme = useTheme(); - const navigate = useNavigate(); - const dispatch = useDispatch(); - const { t } = useTranslation(); - - const handleNavigate = () => { - dispatch(clearAuthState()); - navigate("/login"); - }; - - return ( - - - - - - - {t("common.appName")} - - .MuiStack-root": { - border: 1, - borderRadius: theme.spacing(5), - borderColor: theme.palette.primary.lowContrast, - backgroundColor: theme.palette.primary.main, - padding: { - xs: theme.spacing(12), - sm: theme.spacing(20), - }, - }, - }} - > - - - - - - - - {t("auth.forgotPassword.heading")} - - {t("auth.forgotPassword.subheadings.stepFour")} - - - - - - - - - ), - }} - /> - - - - ); -}; - -export default NewPasswordConfirmed; diff --git a/client/src/Pages/Auth/components/PasswordTooltip.jsx b/client/src/Pages/Auth/components/PasswordTooltip.jsx deleted file mode 100644 index 361850aca..000000000 --- a/client/src/Pages/Auth/components/PasswordTooltip.jsx +++ /dev/null @@ -1,116 +0,0 @@ -import Check from "@/Components/v1/Check/Check.jsx"; -import Stack from "@mui/material/Stack"; -import { Tooltip, useTheme } from "@mui/material"; -import { useTranslation } from "react-i18next"; -import PropTypes from "prop-types"; - -const PasswordTooltip = ({ feedback, form, children }) => { - const theme = useTheme(); - const { t } = useTranslation(); - const hasPassword = form.password.length > 0; - const hasInvalidFeedback = Object.values(feedback).some( - (status) => status !== "success" - ); - const showPasswordTooltip = hasPassword && hasInvalidFeedback; - return ( - - - - - - - - - } - slotProps={{ - tooltip: { - sx: { - backgroundColor: theme.palette.tertiary.main, - border: `0.5px solid ${theme.palette.primary.lowContrast}90`, - borderRadius: theme.spacing(4), - color: theme.palette.primary.contrastText, - width: "auto", - maxWidth: { xs: "25vw", md: "none" }, - whiteSpace: { xs: "normal", md: "nowrap" }, - paddingTop: theme.spacing(8), - px: theme.spacing(8), - }, - }, - arrow: { - sx: { - color: theme.palette.tertiary.main, - }, - }, - }} - > - {children} - - ); -}; - -PasswordTooltip.propTypes = { - feedback: PropTypes.shape({ - length: PropTypes.string, - special: PropTypes.string, - number: PropTypes.string, - uppercase: PropTypes.string, - lowercase: PropTypes.string, - confirm: PropTypes.string, - }), - form: PropTypes.shape({ - password: PropTypes.string.isRequired, - }), - children: PropTypes.node, -}; - -export default PasswordTooltip; diff --git a/client/src/Pages/Auth/hooks/usePasswordFeedback.jsx b/client/src/Pages/Auth/hooks/usePasswordFeedback.jsx deleted file mode 100644 index 73e8fb738..000000000 --- a/client/src/Pages/Auth/hooks/usePasswordFeedback.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import { useState } from "react"; -import { newOrChangedCredentials } from "../../../Validation/validation.js"; - -const usePasswordFeedback = () => { - const [feedback, setFeedback] = useState({}); - const getFeedbackStatus = (form, errors, field, criteria) => { - const fieldErrors = errors?.[field]; - const isFieldEmpty = form?.[field]?.length === 0; - const hasError = fieldErrors?.includes(criteria) || fieldErrors?.includes("empty"); - const isCorrect = !isFieldEmpty && !hasError; - - if (isCorrect) { - return "success"; - } else if (hasError) { - return "error"; - } else { - return "info"; - } - }; - - const handlePasswordFeedback = (updatedForm, name, value, form, errors, setErrors) => { - const validateValue = { [name]: value }; - const validateOptions = { abortEarly: false, context: { password: form.password } }; - if (name === "password" && form.confirm.length > 0) { - validateValue.confirm = form.confirm; - validateOptions.context = { password: value }; - } else if (name === "confirm") { - validateValue.password = form.password; - } - const { error } = newOrChangedCredentials.validate(validateValue, validateOptions); - - const pwdErrors = error?.details.map((error) => ({ - path: error.path[0], - type: error.type, - })); - - const errorsByPath = - pwdErrors && - pwdErrors.reduce((acc, { path, type }) => { - if (!acc[path]) { - acc[path] = []; - } - acc[path].push(type); - return acc; - }, {}); - - const oldErrors = { ...errors }; - if (name === "password") { - oldErrors.password = undefined; - } else if (name === "confirm") { - oldErrors.confirm = undefined; - } - const newErrors = { ...oldErrors, ...errorsByPath }; - - setErrors(newErrors); - - const newFeedback = { - length: getFeedbackStatus(updatedForm, errorsByPath, "password", "string.min"), - special: getFeedbackStatus(updatedForm, errorsByPath, "password", "special"), - number: getFeedbackStatus(updatedForm, errorsByPath, "password", "number"), - uppercase: getFeedbackStatus(updatedForm, errorsByPath, "password", "uppercase"), - lowercase: getFeedbackStatus(updatedForm, errorsByPath, "password", "lowercase"), - confirm: getFeedbackStatus(updatedForm, errorsByPath, "confirm", "different"), - }; - - setFeedback(newFeedback); - }; - return { feedback, handlePasswordFeedback, getFeedbackStatus }; -}; - -export default usePasswordFeedback; diff --git a/client/src/Pages/Auth/hooks/useValidatePassword.jsx b/client/src/Pages/Auth/hooks/useValidatePassword.jsx deleted file mode 100644 index 014804d35..000000000 --- a/client/src/Pages/Auth/hooks/useValidatePassword.jsx +++ /dev/null @@ -1,70 +0,0 @@ -import { useMemo, useState } from "react"; -import { newOrChangedCredentials } from "../../../Validation/validation.js"; - -const getFeedbackStatus = (form, errors, field, criteria) => { - const fieldErrors = errors[field]; - const isFieldEmpty = form[field].length === 0; - const hasError = fieldErrors?.includes(criteria) || fieldErrors?.includes("empty"); - const isCorrect = !isFieldEmpty && !hasError; - - if (isCorrect) { - return "success"; - } else if (hasError) { - return "error"; - } else { - return "info"; - } -}; - -function useValidatePassword() { - const [errors, setErrors] = useState({}); - const [form, setForm] = useState({ - password: "", - confirm: "", - }); - - const handleChange = (event) => { - const { value, name } = event.target; - setForm((prev) => ({ ...prev, [name]: value })); - - const validateValue = { [name]: value }; - const validateOptions = { abortEarly: false, context: { password: form.password } }; - if (name === "password" && form.confirm.length > 0) { - validateValue.confirm = form.confirm; - validateOptions.context = { password: value }; - } else if (name === "confirm") { - validateValue.password = form.password; - } - - const { error } = newOrChangedCredentials.validate(validateValue, validateOptions); - const errors = error?.details.map((error) => ({ - path: error.path[0], - type: error.type, - })); - const errorsByPath = - errors && - errors.reduce((acc, { path, type }) => { - if (!acc[path]) { - acc[path] = []; - } - acc[path].push(type); - return acc; - }, {}); - setErrors(() => (errorsByPath ? { ...errorsByPath } : {})); - }; - - const feedbacks = useMemo( - () => ({ - length: getFeedbackStatus(form, errors, "password", "string.min"), - special: getFeedbackStatus(form, errors, "password", "special"), - number: getFeedbackStatus(form, errors, "password", "number"), - uppercase: getFeedbackStatus(form, errors, "password", "uppercase"), - lowercase: getFeedbackStatus(form, errors, "password", "lowercase"), - confirm: getFeedbackStatus(form, errors, "confirm", "different"), - }), - [form, errors] - ); - - return { handleChange, feedbacks, form, errors }; -} -export { useValidatePassword }; diff --git a/client/src/Pages/Auth/index.css b/client/src/Pages/Auth/index.css deleted file mode 100644 index 86ab05c13..000000000 --- a/client/src/Pages/Auth/index.css +++ /dev/null @@ -1,112 +0,0 @@ -/* AUTH */ -.auth { - height: 100vh; -} -.auth h1 { - font-size: var(--env-var-font-size-xlarge); - font-weight: 600; -} -.auth button:not(.MuiIconButton-root) { - font-size: var(--env-var-font-size-medium-plus); -} - -.auth p + span { - opacity: 0.8; - cursor: pointer; - transition: opacity 300ms ease-in; -} -.auth p > span:not(.email-sent-to) { - text-decoration: underline; - text-underline-offset: 2px; - cursor: pointer; - transition: all 200ms; -} -.auth p > span:not(.email-sent-to):hover { - text-underline-offset: 4px; -} -.auth p + span:hover { - opacity: 1; -} -.auth button:not(.MuiIconButton-root) { - user-select: none; - border-radius: var(--env-var-radius-2); - line-height: 1; -} -.auth .field svg { - width: 24px; - height: 24px; -} -.auth .field h3.MuiTypography-root, -.auth .field .input-error, -.auth .check span.MuiTypography-root { - font-size: var(--env-var-font-size-medium); -} -.auth form + form { - margin: var(--env-var-spacing-1-plus) 0; -} -.auth .email-sent-to { - font-weight: 600; - cursor: default; -} - -.auth > .MuiStack-root:nth-of-type(2) { - height: var(--env-var-nav-bar-height); -} -.auth .background-pattern-svg { - position: absolute; - top: 0; - left: 50%; - transform: translate(-50%, -30%); - z-index: -1; - - width: 100%; - max-width: 800px; - height: 100%; - max-height: 800px; - - background-position: center; - background-size: cover; - background-repeat: no-repeat; -} - -.auth .field { - position: relative; -} -.auth .input-error { - position: absolute; - top: calc(100% - 2px); -} - -@media (max-width: 800px) { - .auth h1 { - font-size: var(--env-var-font-size-large-plus); - } - .auth button:not(.MuiIconButton-root), - .auth .field input, - .auth p, - .auth span { - font-size: var(--env-var-font-size-medium); - } - .auth .check span.MuiTypography-root, - .auth .field .input-error, - .auth .field h3.MuiTypography-root { - font-size: var(--env-var-font-size-small-plus); - } - .auth .check span > span { - display: none; - } - .auth form + form { - margin: var(--env-var-spacing-1-plus) 0; - } - .auth .background-pattern-svg { - max-width: 750px; - max-height: 750px; - } - - .forgot-password-page h1, - .check-email-page h1, - .password-confirmed-page h1, - .set-new-password-page h1 { - font-size: 18px; - } -} diff --git a/client/src/Pages/Auth/old.jsx b/client/src/Pages/Auth/old.jsx deleted file mode 100644 index ada5d6246..000000000 --- a/client/src/Pages/Auth/old.jsx +++ /dev/null @@ -1,283 +0,0 @@ -import { useId } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { useNavigate } from "react-router-dom"; -import { useParams } from "react-router-dom"; -import { useTheme } from "@emotion/react"; -import { Box, Stack, Typography, Button } from "@mui/material"; -import { setNewPassword } from "../../Features/Auth/authSlice.js"; -import { createToast } from "../../Utils/toastUtils.jsx"; -import { newOrChangedCredentials } from "../../Validation/validation.js"; -import Check from "@/Components/v1/Check/Check.jsx"; -import TextInput from "@/Components/v1/Inputs/TextInput/index.jsx"; -import { PasswordEndAdornment } from "@/Components/v1/Inputs/TextInput/Adornments/index.jsx"; -import IconBox from "@/Components/v1/IconBox/index.jsx"; -import Logo from "@/assets/icons/checkmate-icon.svg?react"; -import Background from "@/assets/Images/background-grid.svg?react"; -import Icon from "@/Components/v1/Icon"; -import "./index.css"; -import { useValidatePassword } from "./hooks/useValidatePassword.jsx"; -import { Trans, useTranslation } from "react-i18next"; - -const SetNewPassword = () => { - const navigate = useNavigate(); - const dispatch = useDispatch(); - const theme = useTheme(); - const { t } = useTranslation(); - - const passwordId = useId(); - const confirmPasswordId = useId(); - - const { form, errors, handleChange, feedbacks } = useValidatePassword(); - - const { isLoading } = useSelector((state) => state.auth); - const { token } = useParams(); - - const handleSubmit = async (e) => { - e.preventDefault(); - const { error } = newOrChangedCredentials.validate(form, { - abortEarly: false, - context: { password: form.password }, - }); - - if (error) { - createToast({ - body: - error.details && error.details.length > 0 - ? error.details[0].message // FIXME: Potential untranslated string - : t("auth.common.errors.validation"), - }); - } else { - const action = await dispatch(setNewPassword({ token, form })); - if (action.payload.success) { - navigate("/new-password-confirmed"); - createToast({ - body: t("auth.forgotPassword.toasts.success"), - }); - } else { - const errorMessage = action.payload - ? action.payload.msg // FIXME: Potential untranslated string - : t("auth.forgotPassword.toasts.error"); - createToast({ - body: errorMessage, - }); - } - } - }; - - return ( - - - - - - - {t("common.appName")} - - .MuiStack-root": { - border: 1, - borderRadius: theme.spacing(5), - borderColor: theme.palette.primary.lowContrast, - backgroundColor: theme.palette.primary.main, - padding: { - xs: theme.spacing(12), - sm: theme.spacing(20), - }, - }, - }} - > - - - - - - - - {t("auth.forgotPassword.heading")} - {t("auth.forgotPassword.subheadings.stepThree")} - - - - } - /> - - - } - /> - - - - - - - - - - - - - - - - navigate("/login")} - sx={{ userSelect: "none" }} - /> - ), - }} - /> - - - - ); -}; - -export default SetNewPassword; diff --git a/client/src/Routes/index.jsx b/client/src/Routes/index.jsx index cd585b0f6..9f7238aa5 100644 --- a/client/src/Routes/index.jsx +++ b/client/src/Routes/index.jsx @@ -11,9 +11,7 @@ import NotFound from "../Pages/NotFound/index.jsx"; import AuthLogin from "../Pages/Auth/Login"; import AuthRegister from "@/Pages/Auth/Register"; import AuthForgotPassword from "@/Pages/Auth/Recovery"; -import AuthCheckEmail from "../Pages/Auth/CheckEmail.jsx"; import AuthSetNewPassword from "../Pages/Auth/SetNewPassword"; -import AuthNewPasswordConfirmed from "../Pages/Auth/NewPasswordConfirmed.jsx"; // Uptime import Uptime from "../Pages/Uptime/Monitors"; @@ -417,10 +415,6 @@ const Routes = () => { } /> - } - /> { } /> - } - />