This commit is contained in:
Alex Holliday committed 2026-02-06 20:53:48 +00:00
1 parent 527543d79f
commit ea68e660fc
7 files changed
+166 -6

No files matched your search

@@ -205,12 +205,17 @@ export const MonitorBasePageWithStates = ({
);
};
interface BaseAuthPageProps extends React.PropsWithChildren {
interface BaseAuthPageProps extends React.PropsWithChildren, StackProps {
title: string;
subtitle: string;
}
export const BaseAuthPage = ({ title, subtitle, children }: BaseAuthPageProps) => {
export const BaseAuthPage = ({
title,
subtitle,
children,
...props
}: BaseAuthPageProps) => {
const theme = useTheme();
return (
<BasePage
@@ -220,6 +225,7 @@ export const BaseAuthPage = ({ title, subtitle, children }: BaseAuthPageProps) =
justifyContent={"center"}
minHeight="100vh"
position={"relative"}
{...props}
>
<HeaderAuthControls
hideLogo
@@ -243,7 +249,16 @@ export const BaseAuthPage = ({ title, subtitle, children }: BaseAuthPageProps) =
</Typography>
<Typography variant="h1">{subtitle}</Typography>
</Stack>
{children}
<Stack
gap={theme.spacing(8)}
width={{
xs: "80%",
md: "25%",
lg: "15%",
}}
>
{children}
</Stack>
</BasePage>
);
};
@@ -0,0 +1,42 @@
import Typography from "@mui/material/Typography";
import Stack from "@mui/material/Stack";
import type { StackProps } from "@mui/material/Stack";
import Link from "@mui/material/Link";
import { Link as RouterLink } from "react-router-dom";
import { useTheme } from "@mui/material/styles";
interface TextLinkProps extends StackProps {
text: string;
linkText: string;
href: string;
target?: string;
}
export const TextLink = ({
text,
linkText,
href,
target = "_self",
...props
}: TextLinkProps) => {
const theme = useTheme();
return (
<Stack
direction="row"
gap={theme.spacing(4)}
{...props}
>
<Typography>{text}</Typography>
<Link
color="primary"
to={href}
component={RouterLink}
target={target}
>
{linkText}
</Link>
</Stack>
);
};
@@ -18,3 +18,4 @@ export * from "./BaseChart";
export * from "./Gauge";
export * from "./Tabs";
export * from "./SplitBox";
export * from "./TextLink";
+13
View File
@@ -0,0 +1,13 @@
import { useMemo } from "react";
import { loginSchema, type LoginFormData } from "@/Validation/login";
export const useLoginForm = () => {
return useMemo(() => {
const defaults: LoginFormData = {
email: "",
password: "",
};
return { schema: loginSchema, defaults };
}, []);
};
+68 -2
View File
@@ -1,13 +1,79 @@
import { BaseAuthPage } from "@/Components/v2/design-elements";
import { BaseAuthPage, TextLink } from "@/Components/v2/design-elements";
import { Button, TextField } from "@/Components/v2/inputs";
import { useTranslation } from "react-i18next";
import { useForm, Controller } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod/dist/zod.js";
import { useLoginForm } from "@/Hooks/useLoginForm";
import type { LoginFormData } from "@/Validation/login";
const LoginPage = () => {
const { t } = useTranslation();
const { schema, defaults } = useLoginForm();
const { control, handleSubmit } = useForm<LoginFormData>({
resolver: zodResolver(schema),
defaultValues: defaults,
});
const onSubmit = (data: LoginFormData) => {
console.log("Login submitted:", data);
};
return (
<BaseAuthPage
component="form"
onSubmit={handleSubmit(onSubmit)}
title={t("pages.auth.login.title")}
subtitle={t("pages.auth.login.subtitle")}
></BaseAuthPage>
>
<Controller
name="email"
control={control}
render={({ field, fieldState }) => (
<TextField
{...field}
fieldLabel={t("pages.auth.common.form.option.email.label")}
placeholder={t("pages.auth.common.form.option.email.placeholder")}
error={!!fieldState.error}
helperText={fieldState.error ? t(fieldState.error.message ?? "") : ""}
/>
)}
/>
<Controller
name="password"
control={control}
render={({ field, fieldState }) => (
<TextField
{...field}
type="password"
fieldLabel={t("pages.auth.common.form.option.password.label")}
placeholder={t("pages.auth.common.form.option.password.placeholder")}
error={!!fieldState.error}
helperText={fieldState.error ? t(fieldState.error.message ?? "") : ""}
/>
)}
/>
<Button
variant="contained"
type="submit"
>
{t("pages.auth.login.submit")}
</Button>
<TextLink
alignSelf={"center"}
text={t("pages.auth.login.links.forgotPassword.text")}
linkText={t("pages.auth.login.links.forgotPassword.linkText")}
href="/forgot-password"
/>
<TextLink
alignSelf={"center"}
text={t("pages.auth.login.links.register.text")}
linkText={t("pages.auth.login.links.register.linkText")}
href="/register"
/>
</BaseAuthPage>
);
};
+12
View File
@@ -0,0 +1,12 @@
import { z } from "zod";
export const loginSchema = z.object({
email: z
.string()
.min(1, "auth.common.inputs.email.errors.empty")
.email("auth.common.inputs.email.errors.invalid")
.transform((val) => val.toLowerCase().trim()),
password: z.string().min(1, "auth.common.inputs.password.errors.empty"),
});
export type LoginFormData = z.infer<typeof loginSchema>;
+12 -1
View File
@@ -515,7 +515,18 @@
},
"login": {
"title": "Welcome back to Checkmate!",
"subtitle": "Log in to continue"
"subtitle": "Log in to continue",
"submit": "Login",
"links": {
"forgotPassword": {
"text": "Forgot password?",
"linkText": "Reset password"
},
"register": {
"text": "Don't have an account?",
"linkText": "Register here"
}
}
}
},
"checks": {