Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Header and minor ui changes #654

Merged
merged 3 commits into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added frontend2/public/battlecode-logo-horiz-white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend2/public/battlecode-logo-vert-white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed frontend2/public/favicon.ico
Binary file not shown.
File renamed without changes
Binary file removed frontend2/public/logo192.png
Binary file not shown.
Binary file removed frontend2/public/logo512.png
Binary file not shown.
34 changes: 21 additions & 13 deletions frontend2/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
RouterProvider,
createBrowserRouter,
Navigate,
redirect,
} from "react-router-dom";
import { DEFAULT_EPISODE } from "./utils/constants";
import NotFound from "./views/NotFound";
Expand All @@ -38,31 +39,38 @@ const router = createBrowserRouter([
{ path: "/register", element: <Register /> },
{ path: "/password_forgot", element: <PasswordForgot /> },
{ path: "/password_change", element: <PasswordChange /> },
// Pages that will contain the episode specific sidebar and navbar
{
element: <EpisodeLayout />,
children: [
// Pages that should always be visible
// TODO: /:episodeId/resources, /:episodeId/tournaments, /:episodeId/queue
{ path: "/:episodeId/home", element: <Home /> },
{ path: "/:episodeId/quickstart", element: <QuickStart /> },
{ path: "/:episodeId/*", element: <NotFound /> },
{ path: "/:episodeId/rankings", element: <Rankings /> },
],
},
// Pages that should only be visible when logged in
{
element: <PrivateRoute />,
children: [
{ path: "/account", element: <Account /> },
{
element: <EpisodeLayout />,
children: [
// TODO: /:episodeId/team, /:episodeId/submissions, /:episodeId/scrimmaging
],
},
{ path: "/account", element: <Account /> },
],
},
// Pages that will contain the episode specific sidebar and navbar
{
element: <EpisodeLayout />,
children: [
// Pages that should always be visible
// TODO: /:episodeId/resources, /:episodeId/tournaments, /:episodeId/queue
{ path: "/:episodeId/home", element: <Home /> },
{
path: "/:episodeId/",
loader: ({ params }) => {
return redirect(`/${params.episodeId as string}/home`);
},
},
{ path: "/:episodeId/quickstart", element: <QuickStart /> },
{ path: "/:episodeId/*", element: <NotFound /> },
{ path: "/:episodeId/rankings", element: <Rankings /> },
],
},

// Pages that should redirect
{ path: "/*", element: <Navigate to={`/${DEFAULT_EPISODE}/home`} /> },
]);
Expand Down
3 changes: 3 additions & 0 deletions frontend2/src/components/CurrentUserProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { removeApiTokens } from "../utils/cookies";
import { loginCheck } from "../utils/api/auth";
import { getUserUserProfile } from "../utils/api/user";
import Cookies from "js-cookie";

export const CurrentUserProvider: React.FC<{ children: React.ReactNode }> = ({
children,
Expand All @@ -26,6 +27,8 @@ export const CurrentUserProvider: React.FC<{ children: React.ReactNode }> = ({
});
};
const logout = (): void => {
Cookies.remove("access");
Cookies.remove("refresh");
setUserData({
authState: AuthStateEnum.NOT_AUTHENTICATED,
});
Expand Down
6 changes: 3 additions & 3 deletions frontend2/src/components/EpisodeLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { useContext } from "react";
import Navbar from "./Navbar";
import Header from "./Header";
import Sidebar from "./sidebar";
import { Outlet, useParams } from "react-router-dom";
import { EpisodeContext } from "../contexts/EpisodeContext";

// This component contains the NavBar and SideBar.
// This component contains the Header and SideBar.
// Child route components are rendered with <Outlet />
const EpisodeLayout: React.FC = () => {
const episodeContext = useContext(EpisodeContext);
Expand All @@ -14,7 +14,7 @@ const EpisodeLayout: React.FC = () => {
}
return (
<div className="h-screen">
<Navbar />
<Header />
<div className="flex h-full flex-row">
<Sidebar />
<Outlet />
Expand Down
148 changes: 148 additions & 0 deletions frontend2/src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import React, { Fragment, useContext } from "react";
import { Menu, Transition } from "@headlessui/react";
import { Link, NavLink } from "react-router-dom";
import { AuthStateEnum, useCurrentUser } from "../contexts/CurrentUserContext";
import Icon from "./elements/Icon";
import { EpisodeContext } from "../contexts/EpisodeContext";
import { SIDEBAR_ITEM_DATA } from "./sidebar";

const Header: React.FC = () => {
const { authState, logout, user } = useCurrentUser();
const { episodeId } = useContext(EpisodeContext);

return (
<nav className="bg-gray-700">
<div className="w-full px-2 sm:px-6 lg:px-8">
<div className="relative flex h-16 items-center justify-between">
{/* mobile menu */}
<Menu>
<div className="absolute inset-y-0 left-3 flex items-center sm:hidden">
<Menu.Button className="rounded-md px-1 py-1.5 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white">
<span className="sr-only">Open main menu</span>
<Icon
name="bars_3"
className="text-gray-300 hover:text-white"
size="lg"
/>
</Menu.Button>
</div>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Menu.Items className="absolute left-1 top-12 z-10 mt-2 w-64 gap-2 rounded-md bg-white p-2 font-light shadow-lg sm:hidden">
{SIDEBAR_ITEM_DATA.map(({ iconName, text, linkTo }, index) => (
<Menu.Item key={index}>
<NavLink
className={({ isActive }) =>
"flex items-center gap-3 rounded-lg py-2 pl-1.5 pr-8 text-base " +
(isActive
? "cursor-default text-cyan-600"
: "text-gray-800 ui-active:bg-gray-500 ui-active:text-gray-100")
}
to={`/${episodeId}/${linkTo}`}
>
<Icon name={iconName} size="md" />
{text}
</NavLink>
</Menu.Item>
))}

{authState === AuthStateEnum.NOT_AUTHENTICATED && (
<>
<hr className="m-2" />
<Menu.Item as="div" className="w-full pb-2 pt-1">
<Link
to="/login"
className="rounded-full px-2.5 text-cyan-600 ring-0 hover:bg-gray-100/10"
>
Log in
</Link>
</Menu.Item>
</>
)}
</Menu.Items>
</Transition>
</Menu>
{/* battlecode logo */}
<div className="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start">
<div className="flex flex-shrink-0 items-center">
<img
className="hidden h-8 sm:block"
src="/battlecode-logo-horiz-white.png"
alt="Battlecode Logo"
/>
<img
className="h-10 sm:hidden"
src="/battlecode-logo-vert-white.png"
alt="Battlecode Logo"
/>
</div>
<div className="hidden sm:ml-6 sm:block"></div>
</div>
{/* profile menu (if the user is logged in) */}
{authState === AuthStateEnum.AUTHENTICATED && (
<Menu>
<div className="absolute inset-y-0 right-0 flex items-center pr-2 sm:pr-0">
<Menu.Button className="rounded-full focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800">
<span className="sr-only">Open user menu</span>
<img
className="h-8 w-8 rounded-full bg-white"
src={user?.profile?.avatar_url}
alt="Profile Picture"
/>
</Menu.Button>
</div>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Menu.Items className="absolute right-1 top-12 z-10 mt-2 w-40 rounded-md bg-white py-1 text-gray-800 shadow-lg">
<Menu.Item>
<Link
className="flex w-full items-center rounded-lg px-4 py-2 sm:text-sm "
to="/account"
>
Your profile
</Link>
</Menu.Item>
<Menu.Item>
<button
onClick={logout}
className="flex w-full items-center rounded-lg px-4 py-2 sm:text-sm"
>
Sign out
</button>
</Menu.Item>
</Menu.Items>
</Transition>
</Menu>
)}
{/* sign up / login button */}
{authState === AuthStateEnum.NOT_AUTHENTICATED && (
<div className="absolute inset-y-0 right-1 flex flex-row items-center justify-center gap-3">
<Link
to="/login"
className="hidden rounded-full px-3 py-1.5 text-gray-200 ring-0 hover:bg-gray-100/10 sm:block"
>
Log in
</Link>
<Link
to="/register"
className="rounded-full px-3 py-1.5 text-center text-white ring-2 ring-inset ring-gray-200 hover:bg-gray-100/10"
>
Register
</Link>
</div>
)}
</div>
</div>
</nav>
);
};

export default Header;
7 changes: 0 additions & 7 deletions frontend2/src/components/Navbar.tsx

This file was deleted.

17 changes: 10 additions & 7 deletions frontend2/src/components/elements/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import React from "react";
import Icon, { type IconName } from "./Icon";

const VARIANTS: Record<string, string> = {
"": "bg-gray-50 text-gray-800 hover:bg-gray-100 hover:ring-gray-900 hover:text-black ring-gray-500 ring-1 ring-inset",
dark: "bg-cyan-700 text-white hover:bg-cyan-800",
"light-outline":
"ring-2 ring-inset ring-gray-200 text-gray-200 hover:bg-gray-100/20",
} as const;

interface ButtonProps extends React.ComponentPropsWithoutRef<"button"> {
variant?: string;
label?: string;
Expand All @@ -9,11 +16,6 @@ interface ButtonProps extends React.ComponentPropsWithoutRef<"button"> {
className?: string;
}

const variants: Record<string, string> = {
"": "bg-gray-50 text-gray-800 hover:bg-gray-100 hover:ring-gray-900 hover:text-black ring-gray-500 ring-1 ring-inset",
dark: "bg-cyan-700 text-white hover:bg-cyan-800",
};

const Button: React.FC<ButtonProps> = ({
variant = "",
label,
Expand All @@ -22,14 +24,15 @@ const Button: React.FC<ButtonProps> = ({
className = "",
...rest
}) => {
const variantStyle = `${variants[variant]} ${
const variantStyle = `${VARIANTS[variant]} ${
fullWidth ? "w-full" : ""
} ${className}`;
return (
<button
// default button type
type="button"
className={`flex h-9 flex-row items-center justify-center gap-1.5 rounded px-2.5 py-1.5 font-medium shadow-sm ${variantStyle}`}
className={`flex h-9 flex-row items-center justify-center gap-1.5 rounded px-2.5
py-1.5 shadow-sm ${variantStyle} ${className}`}
{...rest}
>
{iconName !== undefined && <Icon name={iconName} size="sm" />}
Expand Down
12 changes: 9 additions & 3 deletions frontend2/src/components/elements/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ChevronDownIcon as ChevronDownIcon24,
CheckIcon as CheckIcon24,
InformationCircleIcon as InformationCircleIcon24,
Bars3Icon as Bars3Icon24,
} from "@heroicons/react/24/outline";

import {
Expand All @@ -27,6 +28,7 @@ import {
ChevronDownIcon as ChevronDownIcon20,
CheckIcon as CheckIcon20,
InformationCircleIcon as InformationCircleIcon20,
Bars3Icon as Bars3Icon20,
} from "@heroicons/react/20/solid";

const icons24 = {
Expand All @@ -42,6 +44,7 @@ const icons24 = {
chevron_down: ChevronDownIcon24,
check: CheckIcon24,
information_circle: InformationCircleIcon24,
bars_3: Bars3Icon24,
};

const icons20 = {
Expand All @@ -57,28 +60,31 @@ const icons20 = {
chevron_down: ChevronDownIcon20,
check: CheckIcon20,
information_circle: InformationCircleIcon20,
bars_3: Bars3Icon20,
};

export type IconName = keyof typeof icons24 | keyof typeof icons20;

export interface IconProps {
name: IconName;
size?: "sm" | "md" | "xs";
size?: "sm" | "md" | "xs" | "lg";
className?: string;
}

const sizeToClass = {
xs: "h-4 w-4",
sm: "h-5 w-5",
md: "h-6 w-6",
xs: "h-4 w-4",
lg: "h-7 w-7",
};

const Icon: React.FC<IconProps> = ({
name,
size = "md",
className = "",
}: IconProps) => {
const IconComponent = size === "md" ? icons24[name] : icons20[name];
const IconComponent =
size === "md" || size === "lg" ? icons24[name] : icons20[name];
return <IconComponent className={`${sizeToClass[size]} ${className}`} />;
};

Expand Down
8 changes: 4 additions & 4 deletions frontend2/src/components/sidebar/SidebarItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ const SidebarItem: React.FC<SidebarItemProps> = ({
text,
linkTo,
}) => {
const baseStyle = "text-base flex items-center gap-3 ";
const colorVariants = {
gray: "text-gray-800 hover:text-gray-400",
color: "text-teal",
gray: "text-gray-800 hover:text-gray-100 hover:bg-gray-500",
color: "text-cyan-600",
};
return (
<NavLink
className={({ isActive }) =>
baseStyle + (isActive ? colorVariants.color : colorVariants.gray)
"flex items-center gap-3 rounded-lg py-2 pl-1.5 pr-8 text-base " +
(isActive ? colorVariants.color : colorVariants.gray)
}
to={linkTo}
>
Expand Down
6 changes: 3 additions & 3 deletions frontend2/src/components/sidebar/SidebarSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ interface SidebarSectionProps {

const SidebarSection: React.FC<SidebarSectionProps> = ({ children, title }) => {
return (
<div className="pl-5 pr-8">
<div className="px-4">
{title !== undefined && (
<h2 className="mb-3 text-sm uppercase tracking-wider text-gray-500">
<h2 className="mx-auto mb-2 font-light uppercase text-gray-500">
{title}
</h2>
)}
<div className="flex flex-col gap-5">{children}</div>
<div className="flex flex-col gap-1">{children}</div>
</div>
);
};
Expand Down
Loading