Skip to content
193 changes: 158 additions & 35 deletions frontend/src/components/auth/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import React, { useContext, useState, useEffect } from "react";
import { Text, Flex } from "@chakra-ui/react";
import { Redirect } from "react-router-dom";
import {
Button,
Center,
Stack,
Flex,
Box,
Text,
FormLabel,
FormControl,
} from "@chakra-ui/react";
import { isSignInWithEmailLink } from "firebase/auth";
import ResponsiveLogo from "../common/responsive/ResponsiveLogo";
import ResponsiveEmailInput from "../common/responsive/ResponsiveEmailInput";
import ResponsivePasswordInput from "../common/responsive/ResponsivePasswordInput";
import ResponsiveAuthContainer from "../common/responsive/ResponsiveAuthContainer";
import background from "../assets/background.png";
import backgroundMobile from "../assets/background_mobile.png";
import auth from "../../firebase/firebase";
import authAPIClient from "../../APIClients/AuthAPIClient";
import { CREATE_PASSWORD_PAGE, HOME_PAGE } from "../../constants/Routes";
Expand All @@ -13,6 +28,35 @@ const Login = (): React.ReactElement => {
const { authenticatedUser, setAuthenticatedUser } = useContext(AuthContext);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [errorMessage, setErrorMessage] = React.useState("");

const handleEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setEmail(event.target.value);
};
const handlePasswordChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setPassword(event.target.value);
};
const handleForgotPassword = () => {
// Forgot password doesn’t have to route to anything yet
};
const handleLogin = async () => {
setErrorMessage("");
if (!email || !password) {
setErrorMessage("Email and Password are required.");
return;
}
if (!/^\S+@\S+\.\S+$/.test(email)) {
setErrorMessage("Please enter a valid email address.");
return;
}

const user: AuthenticatedUser = await authAPIClient.login(email, password);
setAuthenticatedUser(user);
if (!authenticatedUser) {
setErrorMessage("Invalid login credentials.");
}
};

const [redirectTo, setRedirectTo] = useState<string | null>(null);
const [status, setStatus] = useState<"loading" | "error" | "default">(
"default",
Expand Down Expand Up @@ -53,10 +97,9 @@ const Login = (): React.ReactElement => {
return <Redirect to={redirectTo} />;
}

const onLogInClick = async () => {
const user: AuthenticatedUser = await authAPIClient.login(email, password);
setAuthenticatedUser(user);
};
if (authenticatedUser) {
return <Redirect to="/" />;
}

return (
<>
Expand Down Expand Up @@ -110,36 +153,116 @@ const Login = (): React.ReactElement => {
)}

{status === "default" && !redirectTo && (
<div style={{ textAlign: "center" }}>
<h1>Login</h1>
<form>
<div>
<input
type="email"
value={email}
onChange={(event) => setEmail(event.target.value)}
placeholder="username@domain.com"
/>
</div>
<div>
<input
type="password"
value={password}
onChange={(event) => setPassword(event.target.value)}
placeholder="password"
/>
</div>
<div>
<button
className="btn btn-primary"
type="button"
onClick={onLogInClick}
>
Log In
</button>
</div>
</form>
</div>
<Flex
maxWidth="100vw"
height="100vh"
position="relative"
backgroundRepeat="no-repeat"
backgroundPosition="center"
backgroundSize="cover"
backgroundImage={`url(${backgroundMobile})`}
sx={{
"@media (orientation: landscape)": {
height: "auto",
minHeight: "100vh",
overflowY: "auto",
backgroundImage: `url(${background})`,
},
}}
>
<Center flex="1">
<Flex
gap="2.2rem"
direction="column"
justify="center"
alignItems="center"
padding="1rem"
>
<ResponsiveLogo />
<ResponsiveAuthContainer>
<Text
color="#4A5568"
textStyle={{ base: "h2Mobile", md: "h2" }}
mb="0"
textAlign="center"
>
Welcome Back!
</Text>
<Stack>
<Stack spacing={{ base: "1rem", md: "1.5rem" }} width="100%">
<Box>
<FormLabel
fontSize="14px"
textColor="var(--gray-600, #4A5568)"
lineHeight="8px"
>
Email:
</FormLabel>
<FormControl isInvalid={!!errorMessage}>
<ResponsiveEmailInput
value={email}
onChange={handleEmailChange}
/>
</FormControl>
</Box>
<Box>
<FormLabel
textColor="var(--gray-600, #4A5568)"
fontSize="14px"
lineHeight="8px"
>
Password:
</FormLabel>
<FormControl isInvalid={!!errorMessage}>
<ResponsivePasswordInput
value={password}
onChange={handlePasswordChange}
/>
</FormControl>
</Box>
<Text
cursor="pointer"
fontSize="14px"
onClick={handleForgotPassword}
color="#494B42"
textAlign="center"
_hover={{ textDecoration: "underline" }}
>
Forgot Password?
</Text>
</Stack>

<Box>
<Button
type="submit"
fontSize="14px"
onClick={handleLogin}
color="white"
h="2.4rem"
width="100%"
bg="var(--blue-700, #2C5282)"
>
Login
</Button>
{errorMessage && (
<Box textAlign="center">
<Text
color="red.500"
fontSize="14px"
lineHeight="1"
mb="0"
mt="1rem"
>
{errorMessage}
</Text>
</Box>
)}
</Box>
</Stack>
</ResponsiveAuthContainer>
</Flex>
</Center>
</Flex>
)}
</>
);
Expand Down
24 changes: 24 additions & 0 deletions frontend/src/components/common/responsive/ResponsiveLogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from "react";
import { Center, Image } from "@chakra-ui/react";

const ResponsiveLogo = (): React.ReactElement => {
return (
<Center
height={{ base: "8rem", md: "10.85rem" }}
aspectRatio="27.3/14"
bg="#2C5282"
borderRadius="2.6875rem"
border="1px solid var(--gray-200, #E2E8F0)"
>
<Image
src="/images/humane_society_logo_text.png"
alt="Humane Society Logo"
height={{ base: "6.5rem", md: "9rem" }}
aspectRatio="27.3/14"
objectFit="cover"
/>
</Center>
);
};

export default ResponsiveLogo;
Loading