Skip to content

Commit

Permalink
Add login page (#672)
Browse files Browse the repository at this point in the history
  • Loading branch information
acrantel committed Feb 8, 2024
1 parent 8d47b09 commit 8baa626
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 17 deletions.
2 changes: 1 addition & 1 deletion frontend2/src/index.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import url("https://fonts.googleapis.com/css2?family=Inter&family=Josefin+Sans&display=swap");
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Josefin+Sans:wght@100;300;400;500;700&display=swap');
1 change: 1 addition & 0 deletions frontend2/src/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const DEFAULT_EPISODE = "bc23";
export const FIELD_REQUIRED_ERROR_MSG = "This field is required.";
102 changes: 100 additions & 2 deletions frontend2/src/views/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,105 @@
import React from "react";
import React, { useContext, useEffect, useState } from "react";
import Input from "../components/elements/Input";
import Button from "../components/elements/Button";
import { login as apiLogin } from "../utils/api/auth";
import { type SubmitHandler, useForm } from "react-hook-form";
import { FIELD_REQUIRED_ERROR_MSG } from "../utils/constants";
import { Link, useNavigate } from "react-router-dom";
import { EpisodeContext } from "../contexts/EpisodeContext";
import { useCurrentUser, AuthStateEnum } from "../contexts/CurrentUserContext";
import { type Maybe } from "../utils/utilTypes";
import { getUserUserProfile } from "../utils/api/user";

interface LoginFormInput {
username: string;
password: string;
}

const Login: React.FC = () => {
return <p>login page</p>;
const { register, handleSubmit } = useForm<LoginFormInput>();
const { episodeId } = useContext(EpisodeContext);
const { login, authState } = useCurrentUser();
const [loginError, setLoginError] = useState<Maybe<string>>();

const navigate = useNavigate();

useEffect(() => {
// redirect to home if already logged in
if (authState === AuthStateEnum.AUTHENTICATED) {
navigate(episodeId !== undefined ? `/${episodeId}/home` : "/");
}
}, [authState]);

const onSubmit: SubmitHandler<LoginFormInput> = async (data) => {
try {
setLoginError(undefined);
await apiLogin(data.username, data.password);
login(await getUserUserProfile());
} catch (error) {
console.log(error);
setLoginError(
"Error logging in. Did you enter your username and password correctly?",
);
}
};
return (
<div className="flex h-screen flex-col items-center justify-center bg-gradient-to-tr from-cyan-200 to-cyan-700 p-2">
<div className="mb-6 flex flex-1 items-end text-center font-display text-5xl tracking-wide text-white sm:text-6xl">
BATTLECODE
</div>
{/* https://github.com/orgs/react-hook-form/discussions/8622 */}
<form
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onSubmit={handleSubmit(onSubmit)}
className="flex w-11/12 flex-col gap-5 rounded-lg bg-gray-100 p-6 shadow-md sm:w-[350px]"
>
<h3 className="text-center text-xl font-light text-gray-700">
Log in to Battlecode
</h3>
{
// TODO: replace this with our custom notification component
loginError !== undefined && (
<p className="text-center text-sm text-red-600">{loginError}</p>
)
}
<Input
label="Username"
required
{...register("username", { required: FIELD_REQUIRED_ERROR_MSG })}
/>
<Input
label="Password"
required
{...register("password", { required: FIELD_REQUIRED_ERROR_MSG })}
type="password"
/>
<Button
label="Log in"
fullWidth
className="mt-1"
type="submit"
variant="dark"
/>
<div>
<hr />
<div className="mt-3 flex flex-row justify-between text-sm text-cyan-600">
<Link to="/forgot_password">Forgot password?</Link>
<Link to={episodeId !== undefined ? `/${episodeId}/home` : "/"}>
Back to home
</Link>
</div>
</div>
</form>
<div className="flex-1">
<div className="text-light mt-4 rounded-lg bg-white p-6 text-center text-sm shadow-md sm:w-[350px]">
Need an account?{" "}
<Link className="text-cyan-600" to="/register">
Register for one!
</Link>
</div>
</div>
</div>
);
};

export default Login;
40 changes: 26 additions & 14 deletions frontend2/src/views/Register.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import React, { useState } from "react";
import React, { useContext, useEffect, useState } from "react";
import { createUser } from "../utils/api/user";
import Input from "../components/elements/Input";
import Button from "../components/elements/Button";
import { type SubmitHandler, useForm } from "react-hook-form";
import { useCurrentUser } from "../contexts/CurrentUserContext";
import { COUNTRIES } from "../utils/apiTypes";
import { AuthStateEnum, useCurrentUser } from "../contexts/CurrentUserContext";
import SelectMenu from "../components/elements/SelectMenu";
import { type Maybe } from "../utils/utilTypes";
import {
GenderEnum,
type CountryEnum,
type UserCreateRequest,
} from "../utils/types";

const REQUIRED_ERROR_MSG = "This field is required.";
import { COUNTRIES } from "../utils/apiTypes";
import { FIELD_REQUIRED_ERROR_MSG } from "../utils/constants";
import { useNavigate } from "react-router-dom";
import { EpisodeContext } from "../contexts/EpisodeContext";

const Register: React.FC = () => {
const { login } = useCurrentUser();
const { login, authState } = useCurrentUser();
const navigate = useNavigate();
const { episodeId } = useContext(EpisodeContext);
const {
register,
handleSubmit,
Expand All @@ -29,6 +32,13 @@ const Register: React.FC = () => {
const [country, setCountry] = useState<Maybe<CountryEnum>>();
const [formError, setFormError] = useState<Maybe<string>>();

useEffect(() => {
// redirect to home if already logged in
if (authState === AuthStateEnum.AUTHENTICATED) {
navigate(episodeId !== undefined ? `/${episodeId}/home` : "/");
}
}, [authState]);

const onSubmit: SubmitHandler<UserCreateRequest> = async (data) => {
if (gender === undefined || country === undefined) {
return;
Expand All @@ -54,40 +64,42 @@ const Register: React.FC = () => {
// validate gender and country
await handleSubmit(onSubmit)(event);
if (gender === undefined) {
setError("profile.gender", { message: REQUIRED_ERROR_MSG });
setError("profile.gender", { message: FIELD_REQUIRED_ERROR_MSG });
}
if (country === undefined) {
setError("profile.country", { message: REQUIRED_ERROR_MSG });
setError("profile.country", { message: FIELD_REQUIRED_ERROR_MSG });
}
}}
className="m-6 flex w-11/12 flex-col gap-5 rounded-lg bg-gray-100 p-6 shadow-md sm:w-[550px]"
>
{
// TODO: replace this with our custom notification component
formError !== undefined && <p>{formError}</p>
formError !== undefined && (
<p className="text-center text-sm text-red-600">{formError}</p>
)
}
<Input
required
placeholder="battlecode_player_6.9610"
label="Username"
errorMessage={errors.username?.message}
{...register("username", { required: REQUIRED_ERROR_MSG })}
{...register("username", { required: FIELD_REQUIRED_ERROR_MSG })}
/>
<Input
required
placeholder="************"
label="Password"
type="password"
errorMessage={errors.password?.message}
{...register("password", { required: REQUIRED_ERROR_MSG })}
{...register("password", { required: FIELD_REQUIRED_ERROR_MSG })}
/>
<Input
required
placeholder="player@example.com"
label="Email"
type="email"
errorMessage={errors.email?.message}
{...register("email", { required: REQUIRED_ERROR_MSG })}
{...register("email", { required: FIELD_REQUIRED_ERROR_MSG })}
/>
<div className="grid grid-cols-2 gap-5">
<Input
Expand All @@ -96,15 +108,15 @@ const Register: React.FC = () => {
placeholder="Tim"
label="First name"
errorMessage={errors.first_name?.message}
{...register("first_name", { required: REQUIRED_ERROR_MSG })}
{...register("first_name", { required: FIELD_REQUIRED_ERROR_MSG })}
/>
<Input
className="flex-grow basis-0"
required
placeholder="Beaver"
label="Last name"
errorMessage={errors.last_name?.message}
{...register("last_name", { required: REQUIRED_ERROR_MSG })}
{...register("last_name", { required: FIELD_REQUIRED_ERROR_MSG })}
/>
</div>
{/* begin profile fields */}
Expand Down

0 comments on commit 8baa626

Please sign in to comment.