From 19985a3faa1526807ed33d04524ee928536e90aa Mon Sep 17 00:00:00 2001 From: Rui Lopes Date: Mon, 5 Feb 2024 23:19:51 +0000 Subject: [PATCH] feat: spotlights (#702) Co-authored-by: tiago-bacelar --- components/Dashboard/index.jsx | 2 +- components/Layout/Layout.tsx | 288 +++++--------- components/Layout/components/ActiveLink.tsx | 20 + components/Layout/components/Banner.tsx | 106 ++++++ components/Layout/components/MobileNavbar.tsx | 94 +++++ components/Navbar/index.jsx | 31 +- context/Auth/AuthContext.tsx | 1 + context/Auth/withAuth.js | 1 + context/Notification/NotifyContext.tsx | 39 ++ context/Notification/fetchers.ts | 53 +++ context/Notification/index.ts | 2 + layout/Admin/Spotlights/Spotlights.tsx | 122 ++++++ layout/Admin/Spotlights/index.tsx | 1 + layout/Sponsor/Spotlight/Spotlight.tsx | 36 -- layout/Sponsor/Spotlight/index.ts | 1 - lib/api.js | 15 + lib/time.js | 17 + package-lock.json | 354 +++++++++++++++++- package.json | 4 +- pages/_app.tsx | 7 +- pages/api/notifications.ts | 64 ++++ pages/staff/spotlights.tsx | 1 + 22 files changed, 1009 insertions(+), 250 deletions(-) create mode 100644 components/Layout/components/ActiveLink.tsx create mode 100644 components/Layout/components/Banner.tsx create mode 100644 components/Layout/components/MobileNavbar.tsx create mode 100644 context/Notification/NotifyContext.tsx create mode 100644 context/Notification/fetchers.ts create mode 100644 context/Notification/index.ts create mode 100644 layout/Admin/Spotlights/Spotlights.tsx create mode 100644 layout/Admin/Spotlights/index.tsx delete mode 100644 layout/Sponsor/Spotlight/Spotlight.tsx delete mode 100644 layout/Sponsor/Spotlight/index.ts create mode 100644 lib/time.js create mode 100644 pages/api/notifications.ts create mode 100644 pages/staff/spotlights.tsx diff --git a/components/Dashboard/index.jsx b/components/Dashboard/index.jsx index 43e57c52..54172dae 100644 --- a/components/Dashboard/index.jsx +++ b/components/Dashboard/index.jsx @@ -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); diff --git a/components/Layout/Layout.tsx b/components/Layout/Layout.tsx index 6c030527..cfc40ad1 100644 --- a/components/Layout/Layout.tsx +++ b/components/Layout/Layout.tsx @@ -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 = { @@ -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 ( -
+
+ + - {/* NAVBAR */} - - - {/* OPEN SIDEBAR ON MOBILE */} - - - {/* CONTENT */} -
-

{title}

-

{description}

- - {children} -
-
- ); -} -interface IMobileNavbarProps { - isOpen: boolean; - links: string[]; - currentHref: string; - basePath: string; - onClose: () => void; - onLogout: () => void; -} - -function MobileNavbar({ - isOpen, - links, - currentHref, - basePath, - onClose, - onLogout, -}: IMobileNavbarProps) { - return ( - - - - - - - - - - - - - - ); -} - -interface IActiveLinkProps { - link: string; - href: string; - basePath: string; -} + {/* CONTENT */} +
+

+ {title} +

+

{description}

-function ActiveLink({ link, href, basePath }: IActiveLinkProps) { - const activeStyle = href === `/${basePath}/${link}` && "text-quinary"; - - return ( - - {link} - + {children} +
+
+ ); } diff --git a/components/Layout/components/ActiveLink.tsx b/components/Layout/components/ActiveLink.tsx new file mode 100644 index 00000000..3597cb2f --- /dev/null +++ b/components/Layout/components/ActiveLink.tsx @@ -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} + + ); +} diff --git a/components/Layout/components/Banner.tsx b/components/Layout/components/Banner.tsx new file mode 100644 index 00000000..181edbca --- /dev/null +++ b/components/Layout/components/Banner.tsx @@ -0,0 +1,106 @@ +import { ISpotlight, useNotify } from "@context/Notification"; +import { displayRemainingTime } from "@lib/time"; +import { motion as Motion } from "framer-motion"; +import Link from "next/link"; +import { useEffect, useState } from "react"; + +function PlainBanner({ spotlight }: { spotlight: ISpotlight }) { + const [remaining, setRemaining] = useState(""); + + useEffect(() => { + const timerID = setInterval(() => { + setRemaining(displayRemainingTime(spotlight.end)); + }, 1000); + return () => clearInterval(timerID); + }, []); + + return ( + +
+