Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

feat: spotlights #702

Merged
merged 6 commits into from
Feb 5, 2024
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
2 changes: 1 addition & 1 deletion components/Dashboard/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Dialog, Transition } from "@headlessui/react";

import Return from "/components/Return";

const navigation = ["dashboard", "spotlight"];
const navigation = ["dashboard", "spotlights"];

export default function Dashboard(props) {
const [sidebarOpen, setSidebarOpen] = useState(false);
Expand Down
288 changes: 103 additions & 185 deletions components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,54 @@ import { ReactNode, useState } from "react";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";

import { Dialog, Transition } from "@headlessui/react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faBars, faTimes } from "@fortawesome/free-solid-svg-icons";

import { useAuth } from "@context/Auth";

const roleNavigations = {
sponsor: ["scanner", "visitors"],
attendee: [
"profile",
"slots",
"wheel",
"badgedex",
"leaderboard",
"store",
"inventory",
"identifier",
],
admin: ["scanner", "visitors", "badges", "leaderboard", "users", "events"],
staff: ["badges", "leaderboard", "prizes", "identifier", "cv"],
import { faBars } from "@fortawesome/free-solid-svg-icons";

import { IStaff, IUser, useAuth } from "@context/Auth";
import { ROLES } from "@lib/user";
import MobileNavbar from "./components/MobileNavbar";
import ActiveLink from "./components/ActiveLink";
import Banner from "./components/Banner";
import spotlights from "pages/staff/spotlights";

// FIXME: Normalize user type between moonstone and safira
const basePahts = {
[ROLES.ATTENDEE]: "attendee",
[ROLES.SPONSOR]: "sponsor",
[ROLES.STAFF]: "staff",
};

const roleNavigation = (user: IUser) => {
switch (user.type) {
case ROLES.SPONSOR:
return ["scanner", "visitors"];

case ROLES.ATTENDEE:
return [
"profile",
"wheel",
"badgedex",
"leaderboard",
"store",
"inventory",
"identifier",
];

case ROLES.STAFF:
return [
"leaderboard",
"badges",
"prizes",
"identifier",
"cv",
...((user as IStaff).is_admin ? ["spotlights"] : []),
];

default:
throw new Error(`Unknown USER TYPE: ${user.type}`);
}
};

type LayoutProps = {
Expand All @@ -34,27 +62,20 @@ type LayoutProps = {

export default function Layout({ title, description, children }: LayoutProps) {
const { user, logout } = useAuth();
const [isNavbarOpen, setIsNavbarOpen] = useState(false);
const router = useRouter();

const currentHref = router.asPath;
// FIXME: normalize user type between moonstone and safira
const links =
user.type === "company"
? roleNavigations["sponsor"]
: roleNavigations[user.type];
const basePath = user.type === "company" ? "sponsor" : user.type;

const openNavbar = () => {
setIsNavbarOpen(true);
};
const [isNavbarOpen, setIsNavbarOpen] = useState(false);
const openNavbar = () => setIsNavbarOpen(true);
const closeNavbar = () => setIsNavbarOpen(false);

const closeNavbar = () => {
setIsNavbarOpen(false);
};
const currentHref = router.asPath;
const links = roleNavigation(user);
const basePath = basePahts[user.type];

return (
<div className="text-white lg:flex">
<div className="text-white">
<Banner />

<MobileNavbar
isOpen={isNavbarOpen}
links={links}
Expand All @@ -64,162 +85,59 @@ export default function Layout({ title, description, children }: LayoutProps) {
onLogout={logout}
/>

{/* NAVBAR */}
<aside className="no-scrollbar inset-y-0 hidden w-72 select-none overflow-y-auto border-r-2 bg-secondary px-8 py-5 lg:fixed lg:flex lg:flex-col">
<div className="flex flex-1">
<nav className="mt-8 flex flex-col">
<Link href="/" className="pb-8">
<Image
src="/images/sei-logo.svg"
alt="SEI Logo"
width="220"
height="120"
/>
</Link>

{links.map((link) => (
<ActiveLink
key={link}
link={link}
basePath={basePath}
href={currentHref}
/>
))}
</nav>
</div>
<div className="relative lg:flex">
{/* NAVBAR */}
<aside className="no-scrollbar inset-y-0 hidden w-72 select-none overflow-y-auto border-r-2 bg-secondary px-8 py-5 lg:fixed lg:flex lg:flex-col">
<div className="flex flex-1">
<nav className="mt-8 flex flex-col">
<Link href="/" className="pb-8">
<Image
src="/images/sei-logo.svg"
alt="SEI Logo"
width="220"
height="120"
/>
</Link>

{links.map((link) => (
<ActiveLink
key={link}
link={link}
basePath={basePath}
href={currentHref}
/>
))}
</nav>
</div>

<button
onClick={() => logout()}
className="mt-4 w-full text-left font-iregular text-quinary"
>
Log out 👋
</button>
</aside>

{/* OPEN SIDEBAR ON MOBILE */}
<button
onClick={() => logout()}
className="mt-4 w-full text-left font-iregular text-quinary"
type="button"
onClick={openNavbar}
className="absolute top-4 right-4 text-gray-500 lg:hidden"
>
Log out 👋
<span className="sr-only">Open sidebar</span>
<FontAwesomeIcon icon={faBars} />
</button>
</aside>

{/* OPEN SIDEBAR ON MOBILE */}
<button
type="button"
onClick={openNavbar}
className="absolute top-4 right-4 text-gray-500 lg:hidden"
>
<span className="sr-only">Open sidebar</span>
<FontAwesomeIcon icon={faBars} />
</button>

{/* CONTENT */}
<main className="w-full px-4 pb-6 pt-20 lg:ml-72 lg:px-20">
<h2 className="select-none font-ibold text-4xl sm:text-5xl">{title}</h2>
<p className="mt-2 font-iregular text-lg">{description}</p>

{children}
</main>
</div>
);
}

interface IMobileNavbarProps {
isOpen: boolean;
links: string[];
currentHref: string;
basePath: string;
onClose: () => void;
onLogout: () => void;
}

function MobileNavbar({
isOpen,
links,
currentHref,
basePath,
onClose,
onLogout,
}: IMobileNavbarProps) {
return (
<Transition.Root show={isOpen}>
<Dialog
onClose={onClose}
className="fixed inset-0 z-40 flex w-full text-white md:max-w-md"
>
<Dialog.Panel>
<Transition.Child
enter="transition-opacity ease-linear duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity ease-linear duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Dialog.Overlay className="-z-1 fixed inset-0 bg-gray-600 bg-opacity-75" />
</Transition.Child>

<Transition.Child
enter="transition ease-in-out duration-300 transform"
enterFrom="-translate-x-full"
enterTo="translate-x-0"
leave="transition ease-in-out duration-300 transform"
leaveFrom="translate-x-0"
leaveTo="-translate-x-full"
className="z-1 no-scrollbar absolute h-full w-full justify-between overflow-y-scroll bg-secondary p-8 "
>
<aside className="flex h-full select-none flex-col justify-between">
<div>
<div className="flex items-center justify-between">
<Link href="/" className="font-iregular text-quinary">
<Image
src="/images/sei-logo.svg"
alt="SEI Logo"
width="48"
height="32"
/>
</Link>

<button type="button" onClick={onClose} className="h-12 w-12">
<span className="sr-only">Close sidebar</span>
<FontAwesomeIcon icon={faTimes} />
</button>
</div>

<nav className="flex flex-col">
{links.map((link) => (
<ActiveLink
key={link}
link={link}
basePath={basePath}
href={currentHref}
/>
))}
</nav>
</div>

<button
onClick={onLogout}
className="w-full border-t border-quaternary pt-4 text-left font-iregular text-quinary"
>
Log out 👋
</button>
</aside>
</Transition.Child>
</Dialog.Panel>
</Dialog>
</Transition.Root>
);
}

interface IActiveLinkProps {
link: string;
href: string;
basePath: string;
}
{/* CONTENT */}
<main className="w-full px-4 pb-6 pt-20 lg:ml-72 lg:px-20">
<h2 className="select-none font-ibold text-4xl sm:text-5xl">
{title}
</h2>
<p className="mt-2 font-iregular text-lg">{description}</p>

function ActiveLink({ link, href, basePath }: IActiveLinkProps) {
const activeStyle = href === `/${basePath}/${link}` && "text-quinary";

return (
<Link
href={`/${basePath}/${link}`}
className={`py-8 font-ibold text-xs uppercase transition duration-200 hover:text-quinary ${activeStyle}`}
>
{link}
</Link>
{children}
</main>
</div>
</div>
);
}
20 changes: 20 additions & 0 deletions components/Layout/components/ActiveLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Link from "next/link";

type IActiveLinkProps = {
link: string;
href: string;
basePath: string;
};

export default function ActiveLink({ link, href, basePath }: IActiveLinkProps) {
const activeStyle = href === `/${basePath}/${link}` && "text-quinary";

return (
<Link
href={`/${basePath}/${link}`}
className={`py-8 font-ibold text-xs uppercase transition duration-200 hover:text-quinary ${activeStyle}`}
>
{link}
</Link>
);
}
Loading
Loading