packages/mantine/src/components/pages/auth/components/login/index.tsx
import React from "react";
import {
LoginPageProps,
LoginFormTypes,
useRouterType,
useLink,
useActiveAuthProvider,
} from "@refinedev/core";
import { useLogin, useTranslate, useRouterContext } from "@refinedev/core";
import {
Box,
Card,
Checkbox,
PasswordInput,
Space,
TextInput,
Title,
Anchor,
Button,
Text,
Divider,
Stack,
BoxProps,
CardProps,
useMantineTheme,
} from "@mantine/core";
import { ThemedTitleV2 } from "@components";
import { FormContext } from "@contexts/form-context";
import {
layoutStyles,
cardStyles,
titleStyles,
pageTitleStyles,
} from "../styles";
import { FormPropsType } from "../..";
type LoginProps = LoginPageProps<BoxProps, CardProps, FormPropsType>;
/**
* **refine** has a default login page form which is served on `/login` route when the `authProvider` configuration is provided.
* @see {@link https://refine.dev/docs/api-reference/mantine/components/mantine-auth-page/#login} for more details.
*/
export const LoginPage: React.FC<LoginProps> = ({
providers,
registerLink,
forgotPasswordLink,
rememberMe,
contentProps,
wrapperProps,
renderContent,
formProps,
title,
hideForm,
}) => {
const theme = useMantineTheme();
const { useForm, FormProvider } = FormContext;
const { onSubmit: onSubmitProp, ...useFormProps } = formProps || {};
const translate = useTranslate();
const routerType = useRouterType();
const Link = useLink();
const { Link: LegacyLink } = useRouterContext();
const ActiveLink = routerType === "legacy" ? LegacyLink : Link;
const form = useForm({
initialValues: {
email: "",
password: "",
remember: false,
},
validate: {
email: (value: any) =>
/^\S+@\S+$/.test(value)
? null
: translate("pages.login.errors.validEmail", "Invalid email address"),
password: (value: any) => value === "",
},
...useFormProps,
});
const { onSubmit, getInputProps } = form;
const authProvider = useActiveAuthProvider();
const { mutate: login, isLoading } = useLogin<LoginFormTypes>({
v3LegacyAuthProviderCompatible: Boolean(authProvider?.isLegacy),
});
const PageTitle =
title === false ? null : (
<div style={pageTitleStyles}>
{title ?? <ThemedTitleV2 collapsed={false} />}
</div>
);
const renderProviders = () => {
if (providers && providers.length > 0) {
return (
<>
<Stack spacing={8}>
{providers.map((provider) => {
return (
<Button
key={provider.name}
variant="default"
fullWidth
leftIcon={provider.icon}
onClick={() =>
login({
providerName: provider.name,
})
}
>
{provider.label}
</Button>
);
})}
</Stack>
{!hideForm && (
<Divider
my="md"
labelPosition="center"
label={translate("pages.login.divider", "or")}
/>
)}
</>
);
}
return null;
};
const CardContent = (
<Card style={cardStyles} {...(contentProps ?? {})}>
<Title
style={titleStyles}
color={theme.colorScheme === "dark" ? "brand.5" : "brand.8"}
>
{translate("pages.login.title", "Sign in to your account")}
</Title>
<Space h="sm" />
<Space h="lg" />
{renderProviders()}
{!hideForm && (
<FormProvider form={form}>
<form
onSubmit={onSubmit((values: any) => {
if (onSubmitProp) {
return onSubmitProp(values);
}
return login(values);
})}
>
<TextInput
name="email"
label={translate("pages.login.fields.email", "Email")}
placeholder={translate("pages.login.fields.email", "Email")}
{...getInputProps("email")}
/>
<PasswordInput
name="password"
autoComplete="current-password"
mt="md"
label={translate("pages.login.fields.password", "Password")}
placeholder="●●●●●●●●"
{...getInputProps("password")}
/>
<Box
mt="md"
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
}}
>
{rememberMe ?? (
<Checkbox
label={translate(
"pages.login.buttons.rememberMe",
"Remember me",
)}
size="xs"
{...getInputProps("remember", {
type: "checkbox",
})}
/>
)}
{forgotPasswordLink ?? (
<Anchor
component={ActiveLink as any}
to="/forgot-password"
size="xs"
>
{translate(
"pages.login.buttons.forgotPassword",
"Forgot password?",
)}
</Anchor>
)}
</Box>
<Button
mt="md"
fullWidth
size="md"
type="submit"
loading={isLoading}
>
{translate("pages.login.signin", "Sign in")}
</Button>
</form>
</FormProvider>
)}
{registerLink ?? (
<Text mt="md" size="xs" align="center">
{translate("pages.login.buttons.noAccount", "Don’t have an account?")}{" "}
<Anchor component={ActiveLink as any} to="/register" weight={700}>
{translate("pages.login.signup", "Sign up")}
</Anchor>
</Text>
)}
</Card>
);
return (
<Box
style={{
...layoutStyles,
justifyContent: hideForm ? "flex-start" : layoutStyles.justifyContent,
paddingTop: hideForm ? "15dvh" : layoutStyles.padding,
}}
{...(wrapperProps ?? {})}
>
{renderContent ? (
renderContent(CardContent, PageTitle)
) : (
<>
{PageTitle}
{CardContent}
</>
)}
</Box>
);
};