mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-19 16:08:39 -05:00
Merge pull request #3014 from bluewave-labs/fix/v2/auth-pages
fix/v2: add missing auth elements
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Box from "@mui/material/Box";
|
||||
import { HeaderAuth } from "@/Components/v2/Auth";
|
||||
import Logo from "@/assets/icons/checkmate-icon.svg?react";
|
||||
|
||||
import type { StackProps } from "@mui/material/Stack";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { Typography } from "@mui/material";
|
||||
|
||||
interface AuthBasePageProps extends StackProps {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const AuthBasePage: React.FC<AuthBasePageProps> = ({
|
||||
children,
|
||||
title,
|
||||
subtitle,
|
||||
...props
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Stack
|
||||
gap={theme.spacing(10)}
|
||||
minHeight="100vh"
|
||||
{...props}
|
||||
>
|
||||
<HeaderAuth />
|
||||
<Stack
|
||||
alignItems="center"
|
||||
margin="auto"
|
||||
width="100%"
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<Box
|
||||
width={{ xs: 60, sm: 70, md: 80 }}
|
||||
mb={theme.spacing(10)}
|
||||
>
|
||||
<Logo style={{ width: "100%", height: "100%" }} />
|
||||
</Box>
|
||||
<Typography variant="h1">{title}</Typography>
|
||||
<Typography variant="h1">{subtitle}</Typography>
|
||||
{children}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Logo from "@/assets/icons/checkmate-icon.svg?react";
|
||||
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { LanguageSelector, ThemeSwitch } from "@/Components/v2/Inputs";
|
||||
|
||||
export const HeaderAuth = () => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Stack
|
||||
width={"100%"}
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="flex-end"
|
||||
py={theme.spacing(4)}
|
||||
px={theme.spacing(12)}
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<LanguageSelector />
|
||||
<ThemeSwitch color="red" />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
export { HeaderAuth } from "./HeaderAuth";
|
||||
export { AuthBasePage } from "./AuthBasePage";
|
||||
@@ -0,0 +1,64 @@
|
||||
import "flag-icons/css/flag-icons.min.css";
|
||||
import { Select } from "@/Components/v2/Inputs";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useSelector } from "react-redux";
|
||||
import { setLanguage } from "@/Features/UI/uiSlice";
|
||||
import type { SelectChangeEvent } from "@mui/material/Select";
|
||||
|
||||
export const LanguageSelector = () => {
|
||||
const { i18n } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
const language = useSelector((state: any) => state.ui.language);
|
||||
const languages = Object.keys(i18n.options.resources || {});
|
||||
const languageMap: Record<string, string> = {
|
||||
cs: "cz",
|
||||
ja: "jp",
|
||||
uk: "ua",
|
||||
vi: "vn",
|
||||
};
|
||||
|
||||
const handleChange = (event: SelectChangeEvent<unknown>) => {
|
||||
const newLang = event.target.value;
|
||||
dispatch(setLanguage(newLang));
|
||||
};
|
||||
|
||||
const languagesForDisplay = languages.map((l) => {
|
||||
let formattedLanguage = l === "en" ? "gb" : l;
|
||||
formattedLanguage = formattedLanguage.includes("-")
|
||||
? formattedLanguage.split("-")[1].toLowerCase()
|
||||
: formattedLanguage;
|
||||
formattedLanguage = languageMap[formattedLanguage] || formattedLanguage;
|
||||
const flag = formattedLanguage ? `fi fi-${formattedLanguage}` : null;
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
key={l}
|
||||
value={l}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
{flag && <span className={flag} />}
|
||||
<Typography textTransform={"uppercase"}>{l}</Typography>
|
||||
</Stack>
|
||||
</MenuItem>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Select
|
||||
value={language}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{languagesForDisplay}
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,22 @@
|
||||
import Select from "@mui/material/Select";
|
||||
import type { SelectProps } from "@mui/material/Select";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
|
||||
export const SelectInput: React.FC<SelectProps> = ({ ...props }) => {
|
||||
return <Select {...props} />;
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Select
|
||||
{...props}
|
||||
sx={{
|
||||
height: "34px",
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderColor: theme.palette.primary.lowContrast,
|
||||
},
|
||||
"&:hover .MuiOutlinedInput-notchedOutline": {
|
||||
borderColor: theme.palette.primary.lowContrast,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Link from "@mui/material/Link";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
|
||||
export const TextLink = ({
|
||||
text,
|
||||
linkText,
|
||||
href,
|
||||
target = "_self",
|
||||
}: {
|
||||
text: string;
|
||||
linkText: string;
|
||||
href: string;
|
||||
target?: string;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<Typography>{text}</Typography>
|
||||
<Link
|
||||
color="accent"
|
||||
to={href}
|
||||
component={RouterLink}
|
||||
target={target}
|
||||
>
|
||||
{linkText}
|
||||
</Link>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,135 @@
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { setMode } from "@/Features/UI/uiSlice.js";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
|
||||
const SunAndMoonIcon = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<svg
|
||||
className="sun-and-moon"
|
||||
aria-hidden="true"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<mask
|
||||
className="moon"
|
||||
id="moon-mask"
|
||||
>
|
||||
<rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="100%"
|
||||
height="100%"
|
||||
fill="#fff"
|
||||
/>
|
||||
<circle
|
||||
cx="24"
|
||||
cy="10"
|
||||
r="6"
|
||||
fill="#000"
|
||||
/>
|
||||
</mask>
|
||||
<circle
|
||||
className="sun"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="6"
|
||||
fill={theme.palette.primary.contrastTextSecondary}
|
||||
mask="url(#moon-mask)"
|
||||
/>
|
||||
<g
|
||||
className="sun-beams"
|
||||
stroke={theme.palette.primary.contrastTextSecondary}
|
||||
>
|
||||
<line
|
||||
x1="12"
|
||||
y1="1"
|
||||
x2="12"
|
||||
y2="3"
|
||||
/>
|
||||
<line
|
||||
x1="12"
|
||||
y1="21"
|
||||
x2="12"
|
||||
y2="23"
|
||||
/>
|
||||
<line
|
||||
x1="4.22"
|
||||
y1="4.22"
|
||||
x2="5.64"
|
||||
y2="5.64"
|
||||
/>
|
||||
<line
|
||||
x1="18.36"
|
||||
y1="18.36"
|
||||
x2="19.78"
|
||||
y2="19.78"
|
||||
/>
|
||||
<line
|
||||
x1="1"
|
||||
y1="12"
|
||||
x2="3"
|
||||
y2="12"
|
||||
/>
|
||||
<line
|
||||
x1="21"
|
||||
y1="12"
|
||||
x2="23"
|
||||
y2="12"
|
||||
/>
|
||||
<line
|
||||
x1="4.22"
|
||||
y1="19.78"
|
||||
x2="5.64"
|
||||
y2="18.36"
|
||||
/>
|
||||
<line
|
||||
x1="18.36"
|
||||
y1="5.64"
|
||||
x2="19.78"
|
||||
y2="4.22"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const ThemeSwitch = ({
|
||||
width = 48,
|
||||
height = 48,
|
||||
}: {
|
||||
width?: number;
|
||||
height?: number;
|
||||
}) => {
|
||||
const mode = useSelector((state: any) => state.ui.mode);
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChange = () => {
|
||||
dispatch(setMode(mode === "light" ? "dark" : "light"));
|
||||
};
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
id="theme-toggle"
|
||||
title={t("common.buttons.toggleTheme")}
|
||||
className={`theme-${mode}`}
|
||||
aria-label="auto"
|
||||
aria-live="polite"
|
||||
onClick={handleChange}
|
||||
sx={{
|
||||
width,
|
||||
height,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<SunAndMoonIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
@@ -1,2 +1,7 @@
|
||||
export { ButtonInput as Button } from "./Button";
|
||||
export { ButtonGroupInput as ButtonGroup } from "./ButtonGroup";
|
||||
export { TextInput } from "./TextInput";
|
||||
export { SelectInput as Select } from "./Select";
|
||||
export { LanguageSelector } from "./LanguageSelector";
|
||||
export { ThemeSwitch } from "./ThemeSwitch";
|
||||
export { TextLink } from "./TextLink";
|
||||
|
||||
@@ -35,11 +35,15 @@ export const useGet = <T,>(
|
||||
};
|
||||
};
|
||||
|
||||
export const usePost = <B = any, R = any>(endpoint: string) => {
|
||||
export const usePost = <B = any, R = any>() => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const postFn = async (body: B, config?: AxiosRequestConfig): Promise<R | null> => {
|
||||
const postFn = async (
|
||||
endpoint: string,
|
||||
body: B,
|
||||
config?: AxiosRequestConfig
|
||||
): Promise<R | null> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { AuthBasePage } from "@/Components/v2/Auth";
|
||||
import { Button } from "@/Components/v2/Inputs";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import { TextInput, TextLink } from "@/Components/v2/Inputs";
|
||||
|
||||
import type { ApiResponse } from "@/Hooks/v2/UseApi";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { usePost } from "@/Hooks/v2/UseApi";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { setIsAuthenticated } from "@/Features/Auth/v2AuthSlice";
|
||||
import { useNavigate } from "react-router";
|
||||
import { TextInput } from "@/Components/v2/Inputs/TextInput";
|
||||
import Button from "@mui/material/Button";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import { usePost } from "@/Hooks/v2/UseApi";
|
||||
import type { ApiResponse } from "@/Hooks/v2/UseApi";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { z } from "zod";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
|
||||
const schema = z.object({
|
||||
email: z.email("Invalid email address"),
|
||||
@@ -23,7 +26,7 @@ const Login = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const theme = useTheme();
|
||||
const { post, loading } = usePost<FormData, ApiResponse>("/auth/login");
|
||||
const { post, loading } = usePost<FormData, ApiResponse>();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const {
|
||||
@@ -39,7 +42,7 @@ const Login = () => {
|
||||
});
|
||||
|
||||
const onSubmit = async (data: FormData) => {
|
||||
const result = await post(data);
|
||||
const result = await post("/auth/login", data);
|
||||
if (result) {
|
||||
dispatch(setIsAuthenticated({ authenticated: true }));
|
||||
navigate("/v2/uptime");
|
||||
@@ -49,68 +52,84 @@ const Login = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack
|
||||
alignItems={"center"}
|
||||
justifyContent={"center"}
|
||||
minHeight="100vh"
|
||||
<AuthBasePage
|
||||
title={t("auth.login.welcome")}
|
||||
subtitle={t("auth.login.heading")}
|
||||
>
|
||||
<Stack
|
||||
component="form"
|
||||
padding={theme.spacing(8)}
|
||||
gap={theme.spacing(12)}
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
maxWidth={400}
|
||||
sx={{
|
||||
width: {
|
||||
sm: "80%",
|
||||
md: "70%",
|
||||
lg: "65%",
|
||||
xl: "65%",
|
||||
},
|
||||
}}
|
||||
width={"100%"}
|
||||
alignItems={"center"}
|
||||
justifyContent={"center"}
|
||||
gap={theme.spacing(8)}
|
||||
>
|
||||
<Controller
|
||||
name="email"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
label={t("auth.common.inputs.email.label")}
|
||||
fullWidth
|
||||
placeholder={t("auth.common.inputs.email.placeholder")}
|
||||
error={!!errors.email}
|
||||
helperText={errors.email ? errors.email.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="password"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
type="password"
|
||||
label={t("auth.common.inputs.password.label")}
|
||||
fullWidth
|
||||
placeholder="••••••••••"
|
||||
error={!!errors.password}
|
||||
helperText={errors.password ? errors.password.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
loading={loading}
|
||||
color="accent"
|
||||
type="submit"
|
||||
sx={{ width: "100%", alignSelf: "center", fontWeight: 700 }}
|
||||
<Stack
|
||||
component="form"
|
||||
padding={theme.spacing(8)}
|
||||
gap={theme.spacing(12)}
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
maxWidth={400}
|
||||
sx={{
|
||||
width: {
|
||||
sm: "80%",
|
||||
md: "70%",
|
||||
lg: "65%",
|
||||
xl: "65%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
<Controller
|
||||
name="email"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
label={t("auth.common.inputs.email.label")}
|
||||
fullWidth
|
||||
placeholder={t("auth.common.inputs.email.placeholder")}
|
||||
error={!!errors.email}
|
||||
helperText={errors.email ? errors.email.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="password"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
type="password"
|
||||
label={t("auth.common.inputs.password.label")}
|
||||
fullWidth
|
||||
placeholder="••••••••••"
|
||||
error={!!errors.password}
|
||||
helperText={errors.password ? errors.password.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
loading={loading}
|
||||
color="accent"
|
||||
type="submit"
|
||||
sx={{ width: "100%", alignSelf: "center", fontWeight: 700 }}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
</Stack>
|
||||
<TextLink
|
||||
text={t("auth.login.links.forgotPassword")}
|
||||
linkText={t("auth.login.links.forgotPasswordLink")}
|
||||
href="/forgot-password"
|
||||
/>
|
||||
<TextLink
|
||||
text={t("auth.login.links.register")}
|
||||
linkText={t("auth.login.links.registerLink")}
|
||||
href="/register"
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</AuthBasePage>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { AuthBasePage } from "@/Components/v2/Auth";
|
||||
import { TextInput } from "@/Components/v2/Inputs";
|
||||
import { Button } from "@/Components/v2/Inputs";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Stack from "@mui/material/Stack";
|
||||
|
||||
import type { ApiResponse } from "@/Hooks/v2/UseApi";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import Typography from "@mui/material/Typography";
|
||||
|
||||
import { TextInput } from "@/Components/v2/Inputs/TextInput";
|
||||
import Button from "@mui/material/Button";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import { usePost } from "@/Hooks/v2/UseApi";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
const schema = z
|
||||
.object({
|
||||
@@ -30,14 +33,15 @@ type FormData = z.infer<typeof schema>;
|
||||
const Register = () => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const { post, loading, error } = usePost<FormData>("/auth/register");
|
||||
const navigate = useNavigate();
|
||||
const { post, loading, error } = usePost<FormData, ApiResponse>();
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
control,
|
||||
formState: { errors },
|
||||
} = useForm<FormData>({
|
||||
resolver: zodResolver(schema), // ⬅️ connect Zod
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
email: "",
|
||||
password: "",
|
||||
@@ -45,124 +49,128 @@ const Register = () => {
|
||||
});
|
||||
|
||||
const onSubmit = async (data: FormData) => {
|
||||
const result = await post(data);
|
||||
const result = await post("/auth/register", data);
|
||||
if (result) {
|
||||
console.log(result);
|
||||
navigate("/v2/uptime");
|
||||
} else {
|
||||
console.error("Login failed:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack
|
||||
alignItems={"center"}
|
||||
justifyContent={"center"}
|
||||
minHeight="100vh"
|
||||
<AuthBasePage
|
||||
title={t("auth.registration.welcome")}
|
||||
subtitle={t("auth.registration.heading.user")}
|
||||
>
|
||||
<Stack
|
||||
component="form"
|
||||
padding={theme.spacing(8)}
|
||||
gap={theme.spacing(12)}
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
maxWidth={400}
|
||||
sx={{
|
||||
width: {
|
||||
sm: "80%",
|
||||
md: "70%",
|
||||
lg: "65%",
|
||||
xl: "65%",
|
||||
},
|
||||
}}
|
||||
alignItems={"center"}
|
||||
width={"100%"}
|
||||
>
|
||||
<Controller
|
||||
name="email"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
label={t("auth.common.inputs.email.label")}
|
||||
fullWidth
|
||||
placeholder={t("auth.common.inputs.email.placeholder")}
|
||||
error={!!errors.email}
|
||||
helperText={errors.email ? errors.email.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="firstName"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
label={t("auth.common.inputs.firstName.label")}
|
||||
fullWidth
|
||||
placeholder={t("auth.common.inputs.firstName.placeholder")}
|
||||
error={!!errors.firstName}
|
||||
helperText={errors.firstName ? errors.firstName.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="lastName"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
label={t("auth.common.inputs.lastName.label")}
|
||||
fullWidth
|
||||
placeholder={t("auth.common.inputs.lastName.placeholder")}
|
||||
error={!!errors.lastName}
|
||||
helperText={errors.lastName ? errors.lastName.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="password"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
type="password"
|
||||
label={t("auth.common.inputs.password.label")}
|
||||
fullWidth
|
||||
placeholder="••••••••••"
|
||||
error={!!errors.password}
|
||||
helperText={errors.password ? errors.password.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="confirmPassword"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
type="password"
|
||||
label={t("auth.common.inputs.passwordConfirm.label")}
|
||||
fullWidth
|
||||
placeholder={t("auth.common.inputs.passwordConfirm.placeholder")}
|
||||
error={!!errors.confirmPassword}
|
||||
helperText={errors.confirmPassword ? errors.confirmPassword.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
loading={loading}
|
||||
color="accent"
|
||||
type="submit"
|
||||
sx={{ width: "100%", alignSelf: "center", fontWeight: 700 }}
|
||||
<Stack
|
||||
component="form"
|
||||
padding={theme.spacing(8)}
|
||||
gap={theme.spacing(12)}
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
maxWidth={400}
|
||||
sx={{
|
||||
width: {
|
||||
sm: "80%",
|
||||
md: "70%",
|
||||
lg: "65%",
|
||||
xl: "65%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
{error && <Typography color="error">{error}</Typography>}
|
||||
<Controller
|
||||
name="email"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
label={t("auth.common.inputs.email.label")}
|
||||
fullWidth
|
||||
placeholder={t("auth.common.inputs.email.placeholder")}
|
||||
error={!!errors.email}
|
||||
helperText={errors.email ? errors.email.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="firstName"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
label={t("auth.common.inputs.firstName.label")}
|
||||
fullWidth
|
||||
placeholder={t("auth.common.inputs.firstName.placeholder")}
|
||||
error={!!errors.firstName}
|
||||
helperText={errors.firstName ? errors.firstName.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="lastName"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
label={t("auth.common.inputs.lastName.label")}
|
||||
fullWidth
|
||||
placeholder={t("auth.common.inputs.lastName.placeholder")}
|
||||
error={!!errors.lastName}
|
||||
helperText={errors.lastName ? errors.lastName.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="password"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
type="password"
|
||||
label={t("auth.common.inputs.password.label")}
|
||||
fullWidth
|
||||
placeholder="••••••••••"
|
||||
error={!!errors.password}
|
||||
helperText={errors.password ? errors.password.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="confirmPassword"
|
||||
control={control}
|
||||
defaultValue=""
|
||||
render={({ field }) => (
|
||||
<TextInput
|
||||
{...field}
|
||||
type="password"
|
||||
label={t("auth.common.inputs.passwordConfirm.label")}
|
||||
fullWidth
|
||||
placeholder={t("auth.common.inputs.passwordConfirm.placeholder")}
|
||||
error={!!errors.confirmPassword}
|
||||
helperText={errors.confirmPassword ? errors.confirmPassword.message : ""}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
loading={loading}
|
||||
color="accent"
|
||||
type="submit"
|
||||
sx={{ width: "100%", alignSelf: "center", fontWeight: 700 }}
|
||||
>
|
||||
Register
|
||||
</Button>
|
||||
{error && <Typography color="error">{error}</Typography>}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</AuthBasePage>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ const UptimeCreatePage = () => {
|
||||
mode: "onChange",
|
||||
});
|
||||
const { response } = useGet<ApiResponse>("/notification-channels");
|
||||
const { post, loading, error } = usePost<SubmitValues, ApiResponse>("/monitors");
|
||||
const { post, loading, error } = usePost<SubmitValues>();
|
||||
const selectedType = useWatch({
|
||||
control,
|
||||
name: "type",
|
||||
@@ -58,7 +58,7 @@ const UptimeCreatePage = () => {
|
||||
let interval = humanInterval(data.interval);
|
||||
if (!interval) interval = 60000;
|
||||
const submitData = { ...data, interval };
|
||||
const result = await post(submitData);
|
||||
const result = await post("/monitors", submitData);
|
||||
if (result) {
|
||||
console.log(result);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user