mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-23 02:29:30 -05:00
cleanup
This commit is contained in:
@@ -1,330 +0,0 @@
|
||||
// Components
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import TextInput from "@/Components/v1/Inputs/TextInput/index.jsx";
|
||||
import Button from "@mui/material/Button";
|
||||
import Box from "@mui/material/Box";
|
||||
import PasswordTooltip from "../components/PasswordTooltip.jsx";
|
||||
|
||||
// Utils
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { networkService } from "../../../main.jsx";
|
||||
import { newOrChangedCredentials } from "../../../Validation/validation.js";
|
||||
import { register } from "../../../Features/Auth/authSlice.js";
|
||||
import AuthPageWrapper from "../components/AuthPageWrapper.jsx";
|
||||
import { createToast } from "../../../Utils/toastUtils.jsx";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
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 Register = ({ superAdminExists }) => {
|
||||
// Redux
|
||||
const { isLoading } = useSelector((state) => state.auth);
|
||||
|
||||
// Local state
|
||||
const [form, setForm] = useState({
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
email: "",
|
||||
password: "",
|
||||
confirm: "",
|
||||
role: [],
|
||||
teamId: "",
|
||||
});
|
||||
|
||||
const [errors, setErrors] = useState({});
|
||||
const [feedback, setFeedback] = useState({});
|
||||
|
||||
// Hooks
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const { token } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
// Effects
|
||||
useEffect(() => {
|
||||
const fetchInvite = async () => {
|
||||
if (token !== undefined) {
|
||||
try {
|
||||
const res = await networkService.verifyInvitationToken(token);
|
||||
const invite = res.data.data;
|
||||
const { email } = invite;
|
||||
setForm((prevForm) => {
|
||||
if (!prevForm.email) {
|
||||
return { ...prevForm, email };
|
||||
}
|
||||
return prevForm;
|
||||
});
|
||||
} catch (error) {
|
||||
navigate("/register", { replace: true });
|
||||
}
|
||||
}
|
||||
};
|
||||
fetchInvite();
|
||||
}, [form, token, navigate]);
|
||||
|
||||
// Handlers
|
||||
const onChange = (e) => {
|
||||
let { name, value } = e.target;
|
||||
if (name === "email") value = value.toLowerCase();
|
||||
const updatedForm = { ...form, [name]: value };
|
||||
|
||||
const { error } = newOrChangedCredentials.validate(
|
||||
{ [name]: value },
|
||||
{ abortEarly: false, context: { password: form.password } }
|
||||
);
|
||||
|
||||
setErrors((prev) => ({
|
||||
...prev,
|
||||
[name]: error?.details?.[0]?.message || "",
|
||||
}));
|
||||
|
||||
setForm(updatedForm);
|
||||
};
|
||||
|
||||
const onPasswordChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
const updatedForm = { ...form, [name]: value };
|
||||
setForm(updatedForm);
|
||||
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);
|
||||
};
|
||||
|
||||
const onSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const toSubmit = {
|
||||
...form,
|
||||
role: superAdminExists ? form.role : ["superadmin"],
|
||||
inviteToken: token ? token : "",
|
||||
};
|
||||
|
||||
const { error } = newOrChangedCredentials.validate(toSubmit, {
|
||||
abortEarly: false,
|
||||
context: { password: form.password },
|
||||
});
|
||||
|
||||
if (error) {
|
||||
const formErrors = {};
|
||||
for (const err of error.details) {
|
||||
formErrors[err.path[0]] = err.message;
|
||||
}
|
||||
setErrors(formErrors);
|
||||
return;
|
||||
}
|
||||
|
||||
delete toSubmit.confirm;
|
||||
|
||||
const action = await dispatch(register({ user: toSubmit, token }));
|
||||
if (action.payload.success) {
|
||||
navigate("/uptime");
|
||||
createToast({
|
||||
body: t("auth.registration.toasts.success"),
|
||||
});
|
||||
} else {
|
||||
if (action.payload) {
|
||||
createToast({
|
||||
body: action.payload.msg,
|
||||
});
|
||||
} else {
|
||||
createToast({
|
||||
body: t("common.toasts.unknownError"),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthPageWrapper
|
||||
heading={t("auth.registration.heading.user")}
|
||||
welcome={t("auth.registration.welcome")}
|
||||
>
|
||||
<Stack
|
||||
component="form"
|
||||
width="100%"
|
||||
padding={theme.spacing(8)}
|
||||
gap={theme.spacing(8)}
|
||||
onSubmit={onSubmit}
|
||||
sx={{
|
||||
width: {
|
||||
sm: "80%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography variant="h2">
|
||||
{superAdminExists
|
||||
? t("auth.registration.description.user")
|
||||
: t("auth.registration.description.superAdmin")}
|
||||
</Typography>
|
||||
<Stack
|
||||
direction={{ xs: "column", lg: "row" }}
|
||||
justifyContent="space-between"
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<TextInput
|
||||
name="firstName"
|
||||
sx={{ flex: 1 }}
|
||||
label={t("auth.common.inputs.firstName.label")}
|
||||
width="100%"
|
||||
gap={theme.spacing(4)}
|
||||
isRequired={true}
|
||||
placeholder={t("auth.common.inputs.firstName.placeholder")}
|
||||
autoComplete="given-name"
|
||||
value={form.firstName}
|
||||
onChange={onChange}
|
||||
error={errors.firstName ? true : false}
|
||||
helperText={errors.firstName ? t(errors.firstName) : ""}
|
||||
/>
|
||||
<TextInput
|
||||
name="lastName"
|
||||
label={t("auth.common.inputs.lastName.label")}
|
||||
sx={{ flex: 1 }}
|
||||
width="100%"
|
||||
gap={theme.spacing(4)}
|
||||
isRequired={true}
|
||||
placeholder={t("auth.common.inputs.lastName.placeholder")}
|
||||
autoComplete="family-name"
|
||||
value={form.lastName}
|
||||
onChange={onChange}
|
||||
error={errors.lastName ? true : false}
|
||||
helperText={errors.lastName ? t(errors.lastName) : ""} // Localization keys are in validation.js
|
||||
/>
|
||||
</Stack>
|
||||
<TextInput
|
||||
type="email"
|
||||
name="email"
|
||||
gap={theme.spacing(4)}
|
||||
label={t("auth.common.inputs.email.label")}
|
||||
isRequired={true}
|
||||
placeholder={t("auth.common.inputs.email.placeholder")}
|
||||
autoComplete="email"
|
||||
value={form.email}
|
||||
onInput={(e) => (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
|
||||
/>
|
||||
<PasswordTooltip
|
||||
feedback={feedback}
|
||||
form={form}
|
||||
>
|
||||
<Box>
|
||||
<TextInput
|
||||
type="password"
|
||||
id="register-password-input"
|
||||
name="password"
|
||||
label={t("auth.common.inputs.password.label")}
|
||||
gap={theme.spacing(4)}
|
||||
isRequired={true}
|
||||
placeholder="••••••••••"
|
||||
autoComplete="current-password"
|
||||
value={form.password}
|
||||
onChange={onPasswordChange}
|
||||
error={errors.password && errors.password[0] ? true : false}
|
||||
helperText={
|
||||
errors.password === "auth.common.inputs.password.errors.empty"
|
||||
? t(errors.password)
|
||||
: ""
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
</PasswordTooltip>
|
||||
<TextInput
|
||||
type="password"
|
||||
id="register-confirm-input"
|
||||
name="confirm"
|
||||
label={t("auth.common.inputs.passwordConfirm.label")}
|
||||
gap={theme.spacing(4)}
|
||||
isRequired={true}
|
||||
placeholder={t("auth.common.inputs.passwordConfirm.placeholder")}
|
||||
autoComplete="current-password"
|
||||
value={form.confirm}
|
||||
onChange={onPasswordChange}
|
||||
marginBottom={theme.spacing(4)}
|
||||
error={errors.confirm && errors.confirm[0] ? true : false}
|
||||
/>
|
||||
<Button
|
||||
disabled={isLoading}
|
||||
variant="contained"
|
||||
color="accent"
|
||||
type="submit"
|
||||
sx={{
|
||||
width: "100%",
|
||||
alignSelf: "center",
|
||||
fontWeight: 700,
|
||||
mt: theme.spacing(10),
|
||||
}}
|
||||
>
|
||||
{t("auth.common.navigation.continue")}
|
||||
</Button>
|
||||
</Stack>
|
||||
</AuthPageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
Register.propTypes = {
|
||||
superAdminExists: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default Register;
|
||||
@@ -1,49 +0,0 @@
|
||||
// Components
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Logo from "@/assets/icons/checkmate-icon.svg?react";
|
||||
import LanguageSelector from "@/Components/LanguageSelector.jsx";
|
||||
import ThemeSwitch from "@/Components/v1/ThemeSwitch/index.jsx";
|
||||
|
||||
// Utils
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const AuthHeader = ({ hideLogo = false }) => {
|
||||
// Hooks
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
px={theme.spacing(12)}
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
{!hideLogo && (
|
||||
<>
|
||||
<Logo style={{ borderRadius: theme.shape.borderRadius }} />
|
||||
<Typography sx={{ userSelect: "none" }}>{t("common.appName")}</Typography>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={theme.spacing(2)}
|
||||
alignItems="center"
|
||||
>
|
||||
<LanguageSelector />
|
||||
<ThemeSwitch />
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default AuthHeader;
|
||||
@@ -1,107 +0,0 @@
|
||||
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 "./AuthHeader.jsx";
|
||||
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 (
|
||||
<Stack
|
||||
gap={theme.spacing(10)}
|
||||
minHeight="100vh"
|
||||
position="relative"
|
||||
backgroundColor={theme.palette.primary.main}
|
||||
sx={{ overflow: "hidden" }}
|
||||
>
|
||||
<AuthHeader hideLogo={true} />
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: "0%",
|
||||
transform: "translate(-40%, -40%)",
|
||||
zIndex: 0,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
"& svg g g:last-of-type path": {
|
||||
stroke: theme.palette.primary.lowContrast,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Background style={{ width: "100%" }} />
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
transform: "translate(45%, 55%)",
|
||||
zIndex: 0,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
"& svg g g:last-of-type path": {
|
||||
stroke: theme.palette.primary.lowContrast,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Background style={{ width: "100%" }} />
|
||||
</Box>
|
||||
<Stack
|
||||
backgroundColor={theme.palette.primary.main}
|
||||
sx={{
|
||||
borderRadius: theme.spacing(8),
|
||||
boxShadow: theme.palette.tertiary.cardShadow,
|
||||
margin: "auto",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing(10),
|
||||
padding: theme.spacing(20),
|
||||
zIndex: 1,
|
||||
position: "relative",
|
||||
width: {
|
||||
sm: "60%",
|
||||
md: "50%",
|
||||
lg: "40%",
|
||||
xl: "30%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
mb={theme.spacing(10)}
|
||||
mt={theme.spacing(5)}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: { xs: 60, sm: 70, md: 80 },
|
||||
}}
|
||||
/>
|
||||
<Logo style={{ width: "100%", height: "100%" }} />
|
||||
</Box>
|
||||
<Stack
|
||||
mb={theme.spacing(4)}
|
||||
textAlign="center"
|
||||
>
|
||||
<Typography
|
||||
variant="h1"
|
||||
mb={theme.spacing(2)}
|
||||
>
|
||||
{welcome}
|
||||
</Typography>
|
||||
<Typography variant="h1">{heading}</Typography>
|
||||
</Stack>
|
||||
{children}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default AuthPageWrapper;
|
||||
|
||||
AuthPageWrapper.propTypes = {
|
||||
children: PropTypes.node,
|
||||
heading: PropTypes.node,
|
||||
welcome: PropTypes.node,
|
||||
};
|
||||
@@ -10,7 +10,6 @@ import NotFound from "../Pages/NotFound/index.jsx";
|
||||
// Auth
|
||||
import AuthLogin from "../Pages/Auth/Login";
|
||||
import AuthRegister from "@/Pages/Auth/Register";
|
||||
import OldAuthRegister from "../Pages/Auth/Register/old.jsx";
|
||||
import AuthForgotPassword from "@/Pages/Auth/Recovery";
|
||||
import AuthCheckEmail from "../Pages/Auth/CheckEmail.jsx";
|
||||
import AuthSetNewPassword from "../Pages/Auth/SetNewPassword.jsx";
|
||||
@@ -63,8 +62,7 @@ import CreateMonitor from "@/Pages/CreateMonitor";
|
||||
|
||||
const Routes = () => {
|
||||
const mode = useSelector((state) => state.ui.mode);
|
||||
// const AdminCheckedRegister = withAdminCheck(AuthRegister);
|
||||
const AdminCheckedRegister = AuthRegister;
|
||||
const AdminCheckedRegister = withAdminCheck(AuthRegister);
|
||||
const v2theme = mode === "light" ? lightTheme : darkTheme;
|
||||
return (
|
||||
<LibRoutes>
|
||||
@@ -382,10 +380,6 @@ const Routes = () => {
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/register-old"
|
||||
element={<OldAuthRegister />}
|
||||
/>
|
||||
|
||||
<Route
|
||||
exact
|
||||
|
||||
Reference in New Issue
Block a user