diff --git a/client/src/Components/LanguageSelector.jsx b/client/src/Components/LanguageSelector.jsx index 56a9a898d..9b46204e3 100644 --- a/client/src/Components/LanguageSelector.jsx +++ b/client/src/Components/LanguageSelector.jsx @@ -30,7 +30,17 @@ const LanguageSelector = () => { value={language} onChange={handleChange} size="small" - sx={{ minWidth: 80 }} + sx={{ + minWidth: 80, + "& .MuiSelect-select": { + display: "flex", + alignItems: "center", + justifyContent: "center", + }, + "& .MuiSelect-icon": { + alignSelf: "center", + }, + }} > {languages.map((lang) => { let parsedLang = lang === "en" ? "gb" : lang; @@ -47,11 +57,17 @@ const LanguageSelector = () => { { [name]: error?.details?.[0]?.message || "", })); }; - const onSubmit = async (e) => { e.preventDefault(); const toSubmit = { ...form }; const { error } = loginCredentials.validate(toSubmit, { abortEarly: false }); - if (error) { const formErrors = {}; for (const err of error.details) { @@ -64,7 +58,6 @@ const Login = () => { setErrors(formErrors); return; } - const action = await dispatch(login(form)); if (action.payload.success) { navigate("/uptime"); @@ -89,150 +82,69 @@ const Login = () => { } } }; - return ( - - - - - - - - - - - - - - - - {t("auth.login.welcome")} - - {t("auth.login.heading")} - - - - } - /> - - - - } /> + - + + + ); }; diff --git a/client/src/Pages/Auth/Register/index.jsx b/client/src/Pages/Auth/Register/index.jsx index 97b27e5cf..2b45d2d15 100644 --- a/client/src/Pages/Auth/Register/index.jsx +++ b/client/src/Pages/Auth/Register/index.jsx @@ -1,10 +1,10 @@ // Components import Stack from "@mui/material/Stack"; import Typography from "@mui/material/Typography"; -import AuthHeader from "../components/AuthHeader"; import TextInput from "../../../Components/Inputs/TextInput"; -import Check from "../../../Components/Check/Check"; import Button from "@mui/material/Button"; +import Box from "@mui/material/Box"; +import PasswordTooltip from "../components/PasswordTooltip"; // Utils import { useTheme } from "@emotion/react"; @@ -16,6 +16,7 @@ import { useParams } from "react-router-dom"; import { networkService } from "../../../main"; import { newOrChangedCredentials } from "../../../Validation/validation"; import { register } from "../../../Features/Auth/authSlice"; +import AuthPageWrapper from "../components/AuthPageWrapper"; import { createToast } from "../../../Utils/toastUtils"; import PropTypes from "prop-types"; @@ -195,59 +196,38 @@ const Register = ({ superAdminExists }) => { }; return ( - - - {t("auth.registration.heading.user")} - + + {superAdminExists + ? t("auth.registration.description.user") + : t("auth.registration.description.superAdmin")} + - - {superAdminExists - ? t("auth.registration.heading.user") - : t("auth.registration.heading.superAdmin")} - - - {superAdminExists - ? t("auth.registration.description.user") - : t("auth.registration.description.superAdmin")} - - (e.target.value = e.target.value.toLowerCase())} - onChange={onChange} - error={errors.email ? true : false} - helperText={errors.email ? t(errors.email) : ""} // Localization keys are in validation.js - /> { { error={errors.lastName ? true : false} helperText={errors.lastName ? t(errors.lastName) : ""} // Localization keys are in validation.js /> - - - - - - - - - - - + (e.target.value = e.target.value.toLowerCase())} + onChange={onChange} + error={errors.email ? true : false} + helperText={errors.email ? t(errors.email) : ""} // Localization keys are in validation.js + /> + + + + + + + - + ); }; diff --git a/client/src/Pages/Auth/components/AuthPageWrapper.jsx b/client/src/Pages/Auth/components/AuthPageWrapper.jsx new file mode 100644 index 000000000..b83be19c5 --- /dev/null +++ b/client/src/Pages/Auth/components/AuthPageWrapper.jsx @@ -0,0 +1,107 @@ +import Background from "../../../assets/Images/background-grid.svg?react"; +import Stack from "@mui/material/Stack"; +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import AuthHeader from "../components/AuthHeader"; +import { useTheme } from "@mui/material/styles"; +import Logo from "../../../assets/icons/checkmate-icon.svg?react"; +import PropTypes from "prop-types"; + +const AuthPageWrapper = ({ children, heading, welcome }) => { + const theme = useTheme(); + return ( + + + + + + + + + + + + + + + + {welcome} + + {heading} + + {children} + + + ); +}; + +export default AuthPageWrapper; + +AuthPageWrapper.propTypes = { + children: PropTypes.node, + heading: PropTypes.node, + welcome: PropTypes.node, +}; diff --git a/client/src/Pages/Auth/components/PasswordTooltip.jsx b/client/src/Pages/Auth/components/PasswordTooltip.jsx new file mode 100644 index 000000000..93a74403a --- /dev/null +++ b/client/src/Pages/Auth/components/PasswordTooltip.jsx @@ -0,0 +1,116 @@ +import Check from "../../../Components/Check/Check"; +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.background, + 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.background, + }, + }, + }} + > + {children} + + ); +}; + +PasswordTooltip.propTypes = { + feedback: PropTypes.shape({ + length: PropTypes.string.isRequired, + 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/locales/en.json b/client/src/locales/en.json index 230637474..b287a4229 100644 --- a/client/src/locales/en.json +++ b/client/src/locales/en.json @@ -87,7 +87,7 @@ "highlighted": "one lower character" }, "match": { - "beginning": "Confirm password and password", + "beginning": "Passwords", "highlighted": "must match" }, "number": { @@ -195,7 +195,8 @@ "termsAndPolicies": "By creating an account, you agree to our Terms of Service and Privacy Policy.", "toasts": { "success": "Welcome! Your account was created successfully." - } + }, + "welcome": "Welcome to Checkmate!" } }, "avgCpuTemperature": "Average CPU Temperature",