packages/chakra-ui/src/components/pages/auth/components/login/index.tsx
import React from "react";
import {
LoginPageProps,
LoginFormTypes,
useRouterType,
useLink,
useRouterContext,
useLogin,
useTranslate,
BaseRecord,
HttpError,
useActiveAuthProvider,
} from "@refinedev/core";
import {
Box,
Heading,
BoxProps,
VStack,
Button,
Divider,
FormControl,
FormLabel,
Input,
Link as ChakraLink,
FormErrorMessage,
HStack,
Checkbox,
useColorModeValue,
} from "@chakra-ui/react";
import { useForm } from "@refinedev/react-hook-form";
import { FormProvider } from "react-hook-form";
import { layoutProps, cardProps } from "../styles";
import { FormPropsType } from "../..";
import { ThemedTitleV2 } from "@components";
type LoginProps = LoginPageProps<
BoxProps,
BoxProps,
FormPropsType<LoginFormTypes>
>;
export const LoginPage: React.FC<LoginProps> = ({
providers,
registerLink,
forgotPasswordLink,
rememberMe,
contentProps,
wrapperProps,
renderContent,
formProps,
title,
hideForm,
}) => {
const { onSubmit, ...useFormProps } = formProps || {};
const authProvider = useActiveAuthProvider();
const { mutate: login } = useLogin<LoginFormTypes>({
v3LegacyAuthProviderCompatible: Boolean(authProvider?.isLegacy),
});
const translate = useTranslate();
const routerType = useRouterType();
const NewLink = useLink();
const { Link: LegacyLink } = useRouterContext();
const Link = routerType === "legacy" ? LegacyLink : NewLink;
const methods = useForm<BaseRecord, HttpError, LoginFormTypes>({
...useFormProps,
});
const {
register,
handleSubmit,
formState: { errors },
} = methods;
const renderProviders = () => {
if (providers && providers.length > 0) {
return (
<>
<VStack>
{providers.map((provider) => (
<Button
key={provider.name}
variant="outline"
width="full"
leftIcon={<>{provider?.icon}</>}
fontSize="sm"
onClick={() =>
login({
providerName: provider.name,
})
}
>
{provider.label ?? <label>{provider.label}</label>}
</Button>
))}
</VStack>
{!hideForm && <Divider my="6" />}
</>
);
}
return null;
};
const importantTextColor = useColorModeValue("brand.500", "brand.200");
const PageTitle =
title === false ? null : (
<div
style={{
display: "flex",
justifyContent: "center",
marginBottom: "32px",
fontSize: "20px",
}}
>
{title ?? <ThemedTitleV2 collapsed={false} />}
</div>
);
const allContentProps = { ...cardProps, ...contentProps };
const content = (
<Box
bg="chakra-body-bg"
borderWidth="1px"
borderColor={useColorModeValue("gray.200", "gray.700")}
backgroundColor={useColorModeValue("white", "gray.800")}
{...allContentProps}
>
<Heading
mb="8"
textAlign="center"
fontSize="2xl"
color={importantTextColor}
>
{translate("pages.login.title", "Sign in to your account")}
</Heading>
{renderProviders()}
{!hideForm && (
<form
onSubmit={handleSubmit((data) => {
if (onSubmit) {
return onSubmit(data);
}
return login(data);
})}
>
<FormControl mt="6" isInvalid={!!errors?.email}>
<FormLabel htmlFor="email">
{translate("pages.login.fields.email", "Email")}
</FormLabel>
<Input
id="email"
autoComplete="current-password"
placeholder="Email"
type="text"
{...register("email", {
required: true,
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: translate(
"pages.login.errors.validEmail",
"Invalid email address",
),
},
})}
/>
<FormErrorMessage>{`${errors.email?.message}`}</FormErrorMessage>
</FormControl>
<FormControl mt="6" isInvalid={!!errors?.password}>
<FormLabel htmlFor="password">
{translate("pages.login.fields.password", "Password")}
</FormLabel>
<Input
id="password"
type="password"
placeholder="Password"
{...register("password", {
required: true,
})}
/>
<FormErrorMessage>{`${errors.password?.message}`}</FormErrorMessage>
</FormControl>
{rememberMe ?? (
<Checkbox {...register("remember")} mt="6">
{translate("pages.login.buttons.rememberMe", "Remember me")}
</Checkbox>
)}
<Button mt="6" type="submit" width="full" colorScheme="brand">
{translate("pages.login.signin", "Sign in")}
</Button>
<Box mt="6">
<HStack justifyContent="space-between" fontSize="12px">
{forgotPasswordLink ?? (
<ChakraLink
as={Link}
color={importantTextColor}
to="/forgot-password"
>
{translate(
"pages.login.buttons.forgotPassword",
"Forgot password?",
)}
</ChakraLink>
)}
{registerLink ?? (
<Box>
<span>
{translate(
"pages.login.buttons.noAccount",
"Don’t have an account?",
)}
</span>
<ChakraLink
color={importantTextColor}
ml="1"
as={Link}
fontWeight="bold"
to="/register"
>
{translate("pages.login.register", "Sign up")}
</ChakraLink>
</Box>
)}
</HStack>
</Box>
</form>
)}
{hideForm && registerLink !== false && (
<Box mt={6} textAlign="center">
<span>
{translate(
"pages.login.buttons.noAccount",
"Don’t have an account?",
)}
</span>
<ChakraLink
color={importantTextColor}
ml="1"
as={Link}
fontWeight="bold"
to="/register"
>
{translate("pages.login.register", "Sign up")}
</ChakraLink>
</Box>
)}
</Box>
);
return (
<FormProvider {...methods}>
<Box
style={{
...layoutProps,
justifyContent: hideForm ? "flex-start" : "center",
paddingTop: hideForm ? "15dvh" : "16px",
}}
{...wrapperProps}
>
{renderContent ? (
renderContent(content, PageTitle)
) : (
<>
{PageTitle}
{content}
</>
)}
</Box>
</FormProvider>
);
};