Skip to content

Commit 5a2ef0e

Browse files
committed
Merge branch 'main' into W25/trinity/set-up-colour-theme
2 parents 0992af7 + 00b00f3 commit 5a2ef0e

File tree

3 files changed

+265
-46
lines changed

3 files changed

+265
-46
lines changed

frontend/src/components/auth/Login.tsx

Lines changed: 236 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,270 @@
1-
import React, { useContext, useState } from "react";
1+
import React, { useContext, useState, useEffect } from "react";
22
import { Redirect } from "react-router-dom";
3+
import {
4+
Button,
5+
Center,
6+
Stack,
7+
Flex,
8+
Box,
9+
Text,
10+
FormLabel,
11+
FormControl,
12+
} from "@chakra-ui/react";
313
import { isSignInWithEmailLink } from "firebase/auth";
14+
import ResponsiveLogo from "../common/responsive/ResponsiveLogo";
15+
import ResponsiveEmailInput from "../common/responsive/ResponsiveEmailInput";
16+
import ResponsivePasswordInput from "../common/responsive/ResponsivePasswordInput";
17+
import ResponsiveAuthContainer from "../common/responsive/ResponsiveAuthContainer";
18+
import background from "../assets/background.png";
19+
import backgroundMobile from "../assets/background_mobile.png";
420
import auth from "../../firebase/firebase";
521
import authAPIClient from "../../APIClients/AuthAPIClient";
6-
import { HOME_PAGE } from "../../constants/Routes";
22+
import { CREATE_PASSWORD_PAGE, HOME_PAGE } from "../../constants/Routes";
723
import AuthContext from "../../contexts/AuthContext";
824
import { AuthenticatedUser } from "../../types/AuthTypes";
9-
10-
let didInit = false;
25+
import ResponsiveModalWindow from "../common/responsive/ResponsiveModalWindow";
1126

1227
const Login = (): React.ReactElement => {
1328
const { authenticatedUser, setAuthenticatedUser } = useContext(AuthContext);
1429
const [email, setEmail] = useState("");
1530
const [password, setPassword] = useState("");
16-
const onLogInClick = async () => {
31+
const [errorMessage, setErrorMessage] = React.useState("");
32+
33+
const handleEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
34+
setEmail(event.target.value);
35+
};
36+
const handlePasswordChange = (event: React.ChangeEvent<HTMLInputElement>) => {
37+
setPassword(event.target.value);
38+
};
39+
const handleForgotPassword = () => {
40+
// Forgot password doesn’t have to route to anything yet
41+
};
42+
const handleLogin = async () => {
43+
setErrorMessage("");
44+
if (!email || !password) {
45+
setErrorMessage("Email and Password are required.");
46+
return;
47+
}
48+
if (!/^\S+@\S+\.\S+$/.test(email)) {
49+
setErrorMessage("Please enter a valid email address.");
50+
return;
51+
}
52+
1753
const user: AuthenticatedUser = await authAPIClient.login(email, password);
1854
setAuthenticatedUser(user);
19-
};
20-
const checkIfSignInLink = async () => {
2155
if (!authenticatedUser) {
56+
setErrorMessage("Invalid login credentials.");
57+
}
58+
};
59+
60+
const [redirectTo, setRedirectTo] = useState<string | null>(null);
61+
const [status, setStatus] = useState<"loading" | "error" | "default">(
62+
"default",
63+
);
64+
65+
useEffect(() => {
66+
setStatus("loading");
67+
const checkIfSignInLink = async () => {
2268
const url = window.location.href;
2369
const urlSearchParams = new URLSearchParams(window.location.search);
2470
const signInEmail = urlSearchParams.get("email"); // passed in from actionCode
2571
const isSignInLink = isSignInWithEmailLink(auth, url);
72+
2673
if (signInEmail && isSignInLink) {
2774
const user: AuthenticatedUser = await authAPIClient.loginWithSignInLink(
2875
url,
2976
signInEmail,
3077
);
31-
setAuthenticatedUser(user);
78+
if (user) {
79+
setAuthenticatedUser(user);
80+
setRedirectTo(CREATE_PASSWORD_PAGE);
81+
} else {
82+
setStatus("error");
83+
}
84+
} else {
85+
setStatus("default");
3286
}
87+
};
88+
89+
if (authenticatedUser) {
90+
setRedirectTo(HOME_PAGE);
91+
} else {
92+
checkIfSignInLink();
3393
}
34-
// alert: user is already logged in, please log out before trying again
35-
};
94+
}, [authenticatedUser, setAuthenticatedUser]);
3695

37-
if (authenticatedUser) {
38-
return <Redirect to={HOME_PAGE} />;
96+
if (redirectTo) {
97+
return <Redirect to={redirectTo} />;
3998
}
4099

41-
if (!didInit) {
42-
didInit = true;
43-
checkIfSignInLink();
100+
if (authenticatedUser) {
101+
return <Redirect to="/" />;
44102
}
45103

46104
return (
47-
<div style={{ textAlign: "center" }}>
48-
<h1>Login</h1>
49-
<form>
50-
<div>
51-
<input
52-
type="email"
53-
value={email}
54-
onChange={(event) => setEmail(event.target.value)}
55-
placeholder="username@domain.com"
56-
/>
57-
</div>
58-
<div>
59-
<input
60-
type="password"
61-
value={password}
62-
onChange={(event) => setPassword(event.target.value)}
63-
placeholder="password"
64-
/>
65-
</div>
66-
<div>
67-
<button
68-
className="btn btn-primary"
69-
type="button"
70-
onClick={onLogInClick}
71-
>
72-
Log In
73-
</button>
74-
</div>
75-
</form>
76-
</div>
105+
<>
106+
{status === "loading" && (
107+
<Flex
108+
maxWidth="100vw"
109+
height="100vh"
110+
position="relative"
111+
backgroundRepeat="no-repeat"
112+
backgroundPosition="center"
113+
backgroundSize="cover"
114+
sx={{
115+
"@media (orientation: landscape)": {
116+
height: "auto",
117+
minHeight: "100vh",
118+
overflowY: "auto",
119+
},
120+
}}
121+
>
122+
<ResponsiveModalWindow>
123+
<Text color="#2C5282" textAlign="center">
124+
Loading, please wait...
125+
</Text>
126+
</ResponsiveModalWindow>
127+
</Flex>
128+
)}
129+
130+
{status === "error" && (
131+
<Flex
132+
maxWidth="100vw"
133+
height="100vh"
134+
position="relative"
135+
backgroundRepeat="no-repeat"
136+
backgroundPosition="center"
137+
backgroundSize="cover"
138+
sx={{
139+
"@media (orientation: landscape)": {
140+
height: "auto",
141+
minHeight: "100vh",
142+
overflowY: "auto",
143+
},
144+
}}
145+
>
146+
<ResponsiveModalWindow>
147+
<Text color="red.500" textAlign="center">
148+
An error occurred. If your link is expired, ask an adminstrator
149+
for assistance.
150+
</Text>
151+
</ResponsiveModalWindow>
152+
</Flex>
153+
)}
154+
155+
{status === "default" && !redirectTo && (
156+
<Flex
157+
maxWidth="100vw"
158+
height="100vh"
159+
position="relative"
160+
backgroundRepeat="no-repeat"
161+
backgroundPosition="center"
162+
backgroundSize="cover"
163+
backgroundImage={`url(${backgroundMobile})`}
164+
sx={{
165+
"@media (orientation: landscape)": {
166+
height: "auto",
167+
minHeight: "100vh",
168+
overflowY: "auto",
169+
backgroundImage: `url(${background})`,
170+
},
171+
}}
172+
>
173+
<Center flex="1">
174+
<Flex
175+
gap="2.2rem"
176+
direction="column"
177+
justify="center"
178+
alignItems="center"
179+
padding="1rem"
180+
>
181+
<ResponsiveLogo />
182+
<ResponsiveAuthContainer>
183+
<Text
184+
color="#4A5568"
185+
textStyle={{ base: "h2Mobile", md: "h2" }}
186+
mb="0"
187+
textAlign="center"
188+
>
189+
Welcome Back!
190+
</Text>
191+
<Stack>
192+
<Stack spacing={{ base: "1rem", md: "1.5rem" }} width="100%">
193+
<Box>
194+
<FormLabel
195+
fontSize="14px"
196+
textColor="var(--gray-600, #4A5568)"
197+
lineHeight="8px"
198+
>
199+
Email:
200+
</FormLabel>
201+
<FormControl isInvalid={!!errorMessage}>
202+
<ResponsiveEmailInput
203+
value={email}
204+
onChange={handleEmailChange}
205+
/>
206+
</FormControl>
207+
</Box>
208+
<Box>
209+
<FormLabel
210+
textColor="var(--gray-600, #4A5568)"
211+
fontSize="14px"
212+
lineHeight="8px"
213+
>
214+
Password:
215+
</FormLabel>
216+
<FormControl isInvalid={!!errorMessage}>
217+
<ResponsivePasswordInput
218+
value={password}
219+
onChange={handlePasswordChange}
220+
/>
221+
</FormControl>
222+
</Box>
223+
<Text
224+
cursor="pointer"
225+
fontSize="14px"
226+
onClick={handleForgotPassword}
227+
color="#494B42"
228+
textAlign="center"
229+
_hover={{ textDecoration: "underline" }}
230+
>
231+
Forgot Password?
232+
</Text>
233+
</Stack>
234+
235+
<Box>
236+
<Button
237+
type="submit"
238+
fontSize="14px"
239+
onClick={handleLogin}
240+
color="white"
241+
h="2.4rem"
242+
width="100%"
243+
bg="var(--blue-700, #2C5282)"
244+
>
245+
Login
246+
</Button>
247+
{errorMessage && (
248+
<Box textAlign="center">
249+
<Text
250+
color="red.500"
251+
fontSize="14px"
252+
lineHeight="1"
253+
mb="0"
254+
mt="1rem"
255+
>
256+
{errorMessage}
257+
</Text>
258+
</Box>
259+
)}
260+
</Box>
261+
</Stack>
262+
</ResponsiveAuthContainer>
263+
</Flex>
264+
</Center>
265+
</Flex>
266+
)}
267+
</>
77268
);
78269
};
79270

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from "react";
2+
import { Center, Image } from "@chakra-ui/react";
3+
4+
const ResponsiveLogo = (): React.ReactElement => {
5+
return (
6+
<Center
7+
height={{ base: "8rem", md: "10.85rem" }}
8+
aspectRatio="27.3/14"
9+
bg="#2C5282"
10+
borderRadius="2.6875rem"
11+
border="1px solid var(--gray-200, #E2E8F0)"
12+
>
13+
<Image
14+
src="/images/humane_society_logo_text.png"
15+
alt="Humane Society Logo"
16+
height={{ base: "6.5rem", md: "9rem" }}
17+
aspectRatio="27.3/14"
18+
objectFit="cover"
19+
/>
20+
</Center>
21+
);
22+
};
23+
24+
export default ResponsiveLogo;

frontend/src/components/pages/CreatePasswordPage.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ import {
1010
FormLabel,
1111
FormControl,
1212
} from "@chakra-ui/react";
13+
import { useHistory } from "react-router-dom";
1314
import ResponsiveLogo from "../common/responsive/ResponsiveAuthPageLogo";
1415
import ResponsivePasswordInput from "../common/responsive/ResponsivePasswordInput";
1516
import ResponsiveAuthContainer from "../common/responsive/ResponsiveAuthContainer";
1617
import ResponsiveModalWindow from "../common/responsive/ResponsiveModalWindow";
1718
import background from "../assets/background.png";
1819
import backgroundMobile from "../assets/background_mobile.png";
1920
import AuthAPIClient from "../../APIClients/AuthAPIClient";
21+
import { HOME_PAGE } from "../../constants/Routes";
2022

2123
const CreatePasswordPage = (): React.ReactElement => {
2224
const [showModal, setShowModal] = React.useState(false);
@@ -25,6 +27,8 @@ const CreatePasswordPage = (): React.ReactElement => {
2527
const [errorMessage, setErrorMessage] = React.useState("");
2628
const [email, setEmail] = React.useState("Email not found.");
2729

30+
const history = useHistory();
31+
2832
React.useEffect(() => {
2933
const getEmail = async () => {
3034
const userEmail = await AuthAPIClient.getEmailOfCurrentUser();
@@ -73,7 +77,7 @@ const CreatePasswordPage = (): React.ReactElement => {
7377
};
7478

7579
const handleGetStarted = () => {
76-
// TODO: Navigate to main page
80+
history.push(HOME_PAGE);
7781
};
7882

7983
return (

0 commit comments

Comments
 (0)