From 07e3cb8fc733d8e69618b532b5ecddcaf357fd80 Mon Sep 17 00:00:00 2001 From: tiago-bacelar Date: Tue, 16 Jan 2024 16:33:16 +0000 Subject: [PATCH 1/5] Add TODOs --- components/Layout/Layout.tsx | 25 +++++++++++++++---------- components/Navbar/index.jsx | 5 +++++ components/QRCodeCanvas/index.tsx | 2 +- context/Auth/withAuth.js | 2 ++ lib/user.js | 1 + 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/components/Layout/Layout.tsx b/components/Layout/Layout.tsx index d1e79a5f..4006db5c 100644 --- a/components/Layout/Layout.tsx +++ b/components/Layout/Layout.tsx @@ -7,10 +7,19 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faBars, faTimes } from "@fortawesome/free-solid-svg-icons"; import { useAuth } from "@context/Auth"; +import { ROLES } from "@lib/user"; + +// FIXME: normalize user type between moonstone and safira +const basePaths = { + [ROLES.SPONSOR]: "sponsor", + [ROLES.ATTENDEE]: "attendee", + [ROLES.ADMIN]: "admin", + [ROLES.STAFF]: "staff", +} const roleNavigations = { - sponsor: ["scanner", "visitors"], - attendee: [ + [ROLES.SPONSOR]: ["scanner", "visitors"], + [ROLES.ATTENDEE]: [ "profile", "wheel", "badgedex", @@ -19,8 +28,8 @@ const roleNavigations = { "inventory", "identifier", ], - admin: ["scanner", "visitors", "badges", "leaderboard", "users", "events"], - staff: ["badges", "leaderboard", "prizes", "identifier", "cv"], + [ROLES.ADMIN]: ["scanner", "visitors", "badges", "leaderboard", "users", "events"], + [ROLES.STAFF]: ["badges", "leaderboard", "prizes", "identifier", "cv"], }; type LayoutProps = { @@ -37,12 +46,8 @@ export default function Layout({ title, description, children }: LayoutProps) { 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 links = roleNavigations[user.type]; + const basePath = basePaths[user.type]; const openNavbar = () => { setIsNavbarOpen(true); diff --git a/components/Navbar/index.jsx b/components/Navbar/index.jsx index 7b3ddb6d..fa2c9bfe 100644 --- a/components/Navbar/index.jsx +++ b/components/Navbar/index.jsx @@ -37,6 +37,11 @@ const userNavigation = (type) => { { name: "Scanner", slug: "/sponsor/scanner" }, { name: "Visitors", slug: "/sponsor/visitors" }, ]; + case USER.ROLES.ADMIN: + return [ //TODO + { name: "Scanner", slug: "/sponsor/scanner" }, + { name: "Visitors", slug: "/sponsor/visitors" }, + ]; default: throw new Error(`Unknown USER TYPE: ${type}`); diff --git a/components/QRCodeCanvas/index.tsx b/components/QRCodeCanvas/index.tsx index 538fd837..ade0a2d3 100644 --- a/components/QRCodeCanvas/index.tsx +++ b/components/QRCodeCanvas/index.tsx @@ -10,7 +10,7 @@ const Canvas = ({ uuid }: IProps) => { const [src, setSrc] = useState(""); useEffect(() => { - QRCode.toDataURL(`https://seium.org/attendees/${uuid}`).then(setSrc); + QRCode.toDataURL(`https://seium.org/attendees/${uuid}`).then(setSrc); //TODO: env var }, [uuid]); return qrcode; diff --git a/context/Auth/withAuth.js b/context/Auth/withAuth.js index 114ddac4..cc3fc1d9 100644 --- a/context/Auth/withAuth.js +++ b/context/Auth/withAuth.js @@ -64,6 +64,8 @@ export function withAuth(WrappedComponent) { break; } + //TODO: admin + return ; }; } diff --git a/lib/user.js b/lib/user.js index 62eb6dd5..dd8c172c 100644 --- a/lib/user.js +++ b/lib/user.js @@ -2,4 +2,5 @@ export const ROLES = { ATTENDEE: "attendee", SPONSOR: "company", STAFF: "staff", + ADMIN: "admin", }; From 38ee4c030b5398a54d6f301d982daf68ee4be188 Mon Sep 17 00:00:00 2001 From: tiago-bacelar Date: Wed, 24 Jan 2024 22:49:38 +0000 Subject: [PATCH 2/5] Add new pages skeleton --- components/Layout/Layout.tsx | 14 +++++++++++--- components/Navbar/index.jsx | 9 ++++++--- context/Auth/withAuth.js | 19 +++++++++++++++++-- layout/Admin/BadgeHistory/BadgeHistory.tsx | 11 +++++++++++ layout/Admin/BadgeHistory/index.tsx | 1 + layout/Admin/RedeemHistory/RedeemHistory.tsx | 7 +++++++ layout/Admin/RedeemHistory/index.tsx | 1 + layout/Admin/Spotlight/Spotlight.tsx | 5 +++++ layout/Admin/Spotlight/index.tsx | 1 + pages/admin/badgehistory.tsx | 1 + pages/admin/badges.tsx | 1 + pages/admin/identifier.tsx | 1 + pages/admin/leaderboard.tsx | 1 + pages/admin/prizes/[uuid].tsx | 1 + pages/admin/prizes/index.tsx | 1 + pages/admin/redeemhistory.tsx | 1 + pages/admin/spotlight.tsx | 1 + pages/safira.tsx | 14 ++++++++++++++ public/algoritmos.html | 13 ------------- 19 files changed, 82 insertions(+), 21 deletions(-) create mode 100644 layout/Admin/BadgeHistory/BadgeHistory.tsx create mode 100644 layout/Admin/BadgeHistory/index.tsx create mode 100644 layout/Admin/RedeemHistory/RedeemHistory.tsx create mode 100644 layout/Admin/RedeemHistory/index.tsx create mode 100644 layout/Admin/Spotlight/Spotlight.tsx create mode 100644 layout/Admin/Spotlight/index.tsx create mode 100644 pages/admin/badgehistory.tsx create mode 100644 pages/admin/badges.tsx create mode 100644 pages/admin/identifier.tsx create mode 100644 pages/admin/leaderboard.tsx create mode 100644 pages/admin/prizes/[uuid].tsx create mode 100644 pages/admin/prizes/index.tsx create mode 100644 pages/admin/redeemhistory.tsx create mode 100644 pages/admin/spotlight.tsx create mode 100644 pages/safira.tsx delete mode 100644 public/algoritmos.html diff --git a/components/Layout/Layout.tsx b/components/Layout/Layout.tsx index 4006db5c..3be29573 100644 --- a/components/Layout/Layout.tsx +++ b/components/Layout/Layout.tsx @@ -15,7 +15,7 @@ const basePaths = { [ROLES.ATTENDEE]: "attendee", [ROLES.ADMIN]: "admin", [ROLES.STAFF]: "staff", -} +}; const roleNavigations = { [ROLES.SPONSOR]: ["scanner", "visitors"], @@ -28,8 +28,16 @@ const roleNavigations = { "inventory", "identifier", ], - [ROLES.ADMIN]: ["scanner", "visitors", "badges", "leaderboard", "users", "events"], - [ROLES.STAFF]: ["badges", "leaderboard", "prizes", "identifier", "cv"], + [ROLES.STAFF]: ["leaderboard", "badges", "prizes", "identifier", "cv"], + [ROLES.ADMIN]: [ + "leaderboard", + "badges", + "prizes", + "identifier", + "badgehistory", + "redeemhistory", + "spotlight", + ], }; type LayoutProps = { diff --git a/components/Navbar/index.jsx b/components/Navbar/index.jsx index fa2c9bfe..c1a56262 100644 --- a/components/Navbar/index.jsx +++ b/components/Navbar/index.jsx @@ -38,9 +38,12 @@ const userNavigation = (type) => { { name: "Visitors", slug: "/sponsor/visitors" }, ]; case USER.ROLES.ADMIN: - return [ //TODO - { name: "Scanner", slug: "/sponsor/scanner" }, - { name: "Visitors", slug: "/sponsor/visitors" }, + return [ + { name: "Leaderboard", slug: "/admin/leaderboard" }, + { name: "Give Badges", slug: "/admin/badges" }, + { name: "Give Prizes", slug: "/admin/prizes" }, + { name: "History", slug: "/admin/badgehistory" }, + { name: "Activate Spotlight", slug: "/admin/spotlight" }, ]; default: diff --git a/context/Auth/withAuth.js b/context/Auth/withAuth.js index cc3fc1d9..de0c587a 100644 --- a/context/Auth/withAuth.js +++ b/context/Auth/withAuth.js @@ -62,10 +62,25 @@ export function withAuth(WrappedComponent) { return null; } break; + case USER.ROLES.ADMIN: + if ( + ![ + "/admin/badges", + "/admin/prizes", + "/admin/prizes/[uuid]", + "/admin/identifier", + "/admin/leaderboard", + "/admin/badgehistory", + "/admin/redeemhistory", + "/admin/spotlight", + "/attendees/[uuid]", + ].includes(router.pathname) + ) { + return router.replace("/404"); + } + break; } - //TODO: admin - return ; }; } diff --git a/layout/Admin/BadgeHistory/BadgeHistory.tsx b/layout/Admin/BadgeHistory/BadgeHistory.tsx new file mode 100644 index 00000000..e3b4e912 --- /dev/null +++ b/layout/Admin/BadgeHistory/BadgeHistory.tsx @@ -0,0 +1,11 @@ +import { withAuth } from "@context/Auth"; + +export function BadgeHistory() { + return ( +
+ +
) + ; +} + +export default withAuth(BadgeHistory); diff --git a/layout/Admin/BadgeHistory/index.tsx b/layout/Admin/BadgeHistory/index.tsx new file mode 100644 index 00000000..ae857c09 --- /dev/null +++ b/layout/Admin/BadgeHistory/index.tsx @@ -0,0 +1 @@ +export { default } from "./BadgeHistory"; diff --git a/layout/Admin/RedeemHistory/RedeemHistory.tsx b/layout/Admin/RedeemHistory/RedeemHistory.tsx new file mode 100644 index 00000000..9954e89a --- /dev/null +++ b/layout/Admin/RedeemHistory/RedeemHistory.tsx @@ -0,0 +1,7 @@ +import { withAuth } from "@context/Auth"; + +export function RedeemHistory() { + return
; +} + +export default withAuth(RedeemHistory); diff --git a/layout/Admin/RedeemHistory/index.tsx b/layout/Admin/RedeemHistory/index.tsx new file mode 100644 index 00000000..237da59a --- /dev/null +++ b/layout/Admin/RedeemHistory/index.tsx @@ -0,0 +1 @@ +export { default } from "./RedeemHistory"; diff --git a/layout/Admin/Spotlight/Spotlight.tsx b/layout/Admin/Spotlight/Spotlight.tsx new file mode 100644 index 00000000..dadb964b --- /dev/null +++ b/layout/Admin/Spotlight/Spotlight.tsx @@ -0,0 +1,5 @@ +import { withAuth } from "@context/Auth"; + +export function Spotlight() {} + +export default withAuth(Spotlight); diff --git a/layout/Admin/Spotlight/index.tsx b/layout/Admin/Spotlight/index.tsx new file mode 100644 index 00000000..81eb3ff5 --- /dev/null +++ b/layout/Admin/Spotlight/index.tsx @@ -0,0 +1 @@ +export { default } from "./Spotlight"; diff --git a/pages/admin/badgehistory.tsx b/pages/admin/badgehistory.tsx new file mode 100644 index 00000000..de919921 --- /dev/null +++ b/pages/admin/badgehistory.tsx @@ -0,0 +1 @@ +export { default } from "@layout/Admin/BadgeHistory"; diff --git a/pages/admin/badges.tsx b/pages/admin/badges.tsx new file mode 100644 index 00000000..e5215fda --- /dev/null +++ b/pages/admin/badges.tsx @@ -0,0 +1 @@ +export { default } from "@layout/Staff/Badges"; diff --git a/pages/admin/identifier.tsx b/pages/admin/identifier.tsx new file mode 100644 index 00000000..c8b65c7e --- /dev/null +++ b/pages/admin/identifier.tsx @@ -0,0 +1 @@ +export { default } from "@layout/Staff/Identifier"; diff --git a/pages/admin/leaderboard.tsx b/pages/admin/leaderboard.tsx new file mode 100644 index 00000000..e25d4484 --- /dev/null +++ b/pages/admin/leaderboard.tsx @@ -0,0 +1 @@ +export { default } from "@layout/shared/Leaderboard"; diff --git a/pages/admin/prizes/[uuid].tsx b/pages/admin/prizes/[uuid].tsx new file mode 100644 index 00000000..268f4944 --- /dev/null +++ b/pages/admin/prizes/[uuid].tsx @@ -0,0 +1 @@ +export { default } from "@layout/Staff/Prize"; diff --git a/pages/admin/prizes/index.tsx b/pages/admin/prizes/index.tsx new file mode 100644 index 00000000..dab117c2 --- /dev/null +++ b/pages/admin/prizes/index.tsx @@ -0,0 +1 @@ +export { default } from "@layout/Staff/Prizes"; diff --git a/pages/admin/redeemhistory.tsx b/pages/admin/redeemhistory.tsx new file mode 100644 index 00000000..2cb655db --- /dev/null +++ b/pages/admin/redeemhistory.tsx @@ -0,0 +1 @@ +export { default } from "@layout/Admin/RedeemHistory"; diff --git a/pages/admin/spotlight.tsx b/pages/admin/spotlight.tsx new file mode 100644 index 00000000..20e8e629 --- /dev/null +++ b/pages/admin/spotlight.tsx @@ -0,0 +1 @@ +export { default } from "@layout/Admin/Spotlight"; diff --git a/pages/safira.tsx b/pages/safira.tsx new file mode 100644 index 00000000..3f838804 --- /dev/null +++ b/pages/safira.tsx @@ -0,0 +1,14 @@ +function Safira() { + return null; +} + +export async function getServerSideProps() { + return { + redirect: { + destination: "https://www.youtube.com/watch?v=YvrWzbp7B4g", + permanent: false, + }, + }; +} + +export default Safira; diff --git a/public/algoritmos.html b/public/algoritmos.html deleted file mode 100644 index 40aa15b9..00000000 --- a/public/algoritmos.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - Um meme da SEI - - - - Não passaste a algoritmos - - From 0b8a390ab61a1485689b004586f9016782e51554 Mon Sep 17 00:00:00 2001 From: tiago-bacelar Date: Thu, 1 Feb 2024 18:48:05 +0000 Subject: [PATCH 3/5] Add helpful components --- components/AttendeeMention/index.tsx | 26 +++ components/Layout/Layout.tsx | 61 +++--- components/Navbar/index.jsx | 22 +- components/PaginatedSearch/index.tsx | 32 +++ components/Table/TableColumn.tsx | 61 ++++++ components/Table/index.tsx | 39 ++++ context/Auth/AuthContext.tsx | 16 +- context/Auth/index.ts | 2 + context/Auth/withAuth.js | 22 +- layout/Admin/BadgeHistory/BadgeHistory.tsx | 114 +++++++++- layout/Admin/RedeemHistory/RedeemHistory.tsx | 216 ++++++++++++++++++- layout/Admin/Spotlight/Spotlight.tsx | 9 +- layout/Login/Login.tsx | 22 +- lib/user.js | 1 - pages/admin/badges.tsx | 1 - pages/admin/identifier.tsx | 1 - pages/admin/leaderboard.tsx | 1 - pages/admin/prizes/[uuid].tsx | 1 - pages/admin/prizes/index.tsx | 1 - pages/{admin => staff}/badgehistory.tsx | 0 pages/{admin => staff}/redeemhistory.tsx | 0 pages/{admin => staff}/spotlight.tsx | 0 tailwind.config.js | 21 ++ 23 files changed, 592 insertions(+), 77 deletions(-) create mode 100644 components/AttendeeMention/index.tsx create mode 100644 components/PaginatedSearch/index.tsx create mode 100644 components/Table/TableColumn.tsx create mode 100644 components/Table/index.tsx delete mode 100644 pages/admin/badges.tsx delete mode 100644 pages/admin/identifier.tsx delete mode 100644 pages/admin/leaderboard.tsx delete mode 100644 pages/admin/prizes/[uuid].tsx delete mode 100644 pages/admin/prizes/index.tsx rename pages/{admin => staff}/badgehistory.tsx (100%) rename pages/{admin => staff}/redeemhistory.tsx (100%) rename pages/{admin => staff}/spotlight.tsx (100%) diff --git a/components/AttendeeMention/index.tsx b/components/AttendeeMention/index.tsx new file mode 100644 index 00000000..cd7f4f0c --- /dev/null +++ b/components/AttendeeMention/index.tsx @@ -0,0 +1,26 @@ +import { IAttendee, useAuth } from "@context/Auth"; +import { ROLES } from "@lib/user"; +import Link from "next/link"; + +interface Mention { + nickname: string; + name: string; +} + +export default function AttendeeMention({ attendee }: { attendee: Mention }) { + const { user } = useAuth(); + const isSelf = + user && + user.type === ROLES.ATTENDEE && + (user as IAttendee).nickname === attendee.nickname; + + return ( + + + {attendee.name} + + + ); +} diff --git a/components/Layout/Layout.tsx b/components/Layout/Layout.tsx index 3be29573..9da12491 100644 --- a/components/Layout/Layout.tsx +++ b/components/Layout/Layout.tsx @@ -6,39 +6,50 @@ 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"; +import { IStaff, IUser, useAuth } from "@context/Auth"; import { ROLES } from "@lib/user"; // FIXME: normalize user type between moonstone and safira const basePaths = { [ROLES.SPONSOR]: "sponsor", [ROLES.ATTENDEE]: "attendee", - [ROLES.ADMIN]: "admin", [ROLES.STAFF]: "staff", }; -const roleNavigations = { - [ROLES.SPONSOR]: ["scanner", "visitors"], - [ROLES.ATTENDEE]: [ - "profile", - "wheel", - "badgedex", - "leaderboard", - "store", - "inventory", - "identifier", - ], - [ROLES.STAFF]: ["leaderboard", "badges", "prizes", "identifier", "cv"], - [ROLES.ADMIN]: [ - "leaderboard", - "badges", - "prizes", - "identifier", - "badgehistory", - "redeemhistory", - "spotlight", - ], -}; +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 ? [ + "badgehistory", + "redeemhistory", + "spotlight", + ] : []) + ]; + + default: + throw new Error(`Unknown USER TYPE: ${user.type}`); + } +} type LayoutProps = { title?: string; @@ -54,7 +65,7 @@ export default function Layout({ title, description, children }: LayoutProps) { const router = useRouter(); const currentHref = router.asPath; - const links = roleNavigations[user.type]; + const links = roleNavigation(user); const basePath = basePaths[user.type]; const openNavbar = () => { diff --git a/components/Navbar/index.jsx b/components/Navbar/index.jsx index c1a56262..312f3b30 100644 --- a/components/Navbar/index.jsx +++ b/components/Navbar/index.jsx @@ -21,8 +21,8 @@ const navigation = [ { name: "FAQs", slug: "/faqs" }, ]; -const userNavigation = (type) => { - switch (type) { +const userNavigation = (user) => { + switch (user.type) { case USER.ROLES.ATTENDEE: return [{ name: "Dashboard", slug: "/attendee/profile" }]; case USER.ROLES.STAFF: @@ -31,23 +31,17 @@ const userNavigation = (type) => { { name: "Give Badges", slug: "/staff/badges" }, { name: "Give Prizes", slug: "/staff/prizes" }, { name: "Upload CV", slug: "/staff/cv" }, + ...(user.is_admin ? [ + { name: "Manage Spotlight", slug: "/staff/spotlight" }, + ] : []) ]; case USER.ROLES.SPONSOR: return [ { name: "Scanner", slug: "/sponsor/scanner" }, { name: "Visitors", slug: "/sponsor/visitors" }, ]; - case USER.ROLES.ADMIN: - return [ - { name: "Leaderboard", slug: "/admin/leaderboard" }, - { name: "Give Badges", slug: "/admin/badges" }, - { name: "Give Prizes", slug: "/admin/prizes" }, - { name: "History", slug: "/admin/badgehistory" }, - { name: "Activate Spotlight", slug: "/admin/spotlight" }, - ]; - default: - throw new Error(`Unknown USER TYPE: ${type}`); + throw new Error(`Unknown USER TYPE: ${user.type}`); } }; @@ -130,7 +124,7 @@ export default function Navbar({ bgColor, fgColor, button, children }) { > {user && - userNavigation(user.type).map((item) => ( + userNavigation(user).map((item) => ( ( + userNavigation(user).map((item) => ( ({ + limit, + fetchElems, + showElems, + children +} : { + limit: number, + fetchElems: (query: string, offset: number, limit: number) => Promise | Elem[], + showElems: (elems : Elem[]) => ReactElement + children?: ReactElement +}) { + const [ query, updateQuery ] = useState(""); + const [ offset, updateOffset ] = useState(0); + const [ elems, updateElems ] = useState([]); + + useEffect(() => { + (async () => { + const fetched = await fetchElems(query, offset, limit); + updateElems(fetched); + })(); + }, [query, offset]); + + return ( + <> + { /*TODO: search bar and pagination*/ } + { children } + { showElems(elems) } + + ); +} \ No newline at end of file diff --git a/components/Table/TableColumn.tsx b/components/Table/TableColumn.tsx new file mode 100644 index 00000000..08d4499d --- /dev/null +++ b/components/Table/TableColumn.tsx @@ -0,0 +1,61 @@ +import { FunctionComponent, Key, ReactNode, createElement } from "react"; + +export enum Align { + Left, + Center, + Right +} + +export function alignToCSS(align: Align): string { + switch (+align) { + case Align.Left: return "text-left justify-self-left"; + case Align.Center: return "text-center justify-self-center"; + case Align.Right: return "text-right justify-self-right"; + } +} + +export type TableColumnProps = { + key?: Key; + header?: string; + headerAlign?: Align; + elemAlign?: Align; + elemPadding?: number; + colSpan?: number; + getter?: (e: T) => ReactNode; +}; + +type ResolvedTableColumnProps = { + key: Key; + header: string; + headerAlign: Align; + elemAlign: Align; + elemPadding: number; + colSpan: number; + getter: (e: T) => ReactNode; +}; + +export function resolveProps(props: TableColumnProps): ResolvedTableColumnProps { + return { + key: Math.random(), + header: "", + headerAlign: Align.Center, + elemAlign: Align.Center, + elemPadding: 0, + colSpan: 1, + getter: (x) => x as ReactNode, + ...props + }; +} + +export function printHeader(props : TableColumnProps) { + return ( +
+ { props.header } +
+ ); +} + + +export default function TableColumn(props : TableColumnProps) { + return createElement>(TableColumn, props); +} \ No newline at end of file diff --git a/components/Table/index.tsx b/components/Table/index.tsx new file mode 100644 index 00000000..55622b04 --- /dev/null +++ b/components/Table/index.tsx @@ -0,0 +1,39 @@ +import { Key, ReactElement } from "react"; +import TableColumn, { Align, TableColumnProps, alignToCSS, printHeader, resolveProps } from "./TableColumn"; + +export { TableColumn, Align }; + +type TableProps = { + children: ReactElement>[]; + elems: T[]; + elemKey?: (elem: T) => Key; +}; + +export default function Table({ children, elems, elemKey } : TableProps) { + const cols = children.map((c) => resolveProps(c.props)); + const totalColSpan = cols.reduce((sum, c) => sum + c.colSpan, 0); + const anyHasHeader = cols.reduce((or, c) => c.header || or, false); + + const printElem = (elem: T, isFirst: boolean, isLast: boolean) => { + const border = isLast ? "" : "boprder-b-solid border-b-2"; + const key = elemKey ? elemKey(elem) : Math.random(); + + return
+ { cols.map((c) => +
+ { c.getter(elem) } +
) + } +
; + } + + return <> + { + anyHasHeader && +
+ { cols.map(printHeader) } +
+ } + { elems.map((e, i) => printElem(e, i === 0, i === elems.length - 1)) } + ; +} \ No newline at end of file diff --git a/context/Auth/AuthContext.tsx b/context/Auth/AuthContext.tsx index ef861ac8..d92ff1f0 100644 --- a/context/Auth/AuthContext.tsx +++ b/context/Auth/AuthContext.tsx @@ -28,12 +28,21 @@ export interface IPrize { not_redeemed: number; } +export interface IRedeemable { + id: number; + image: string; + name: string; + not_redeemed: number; + price: number; + quantity: number; +} + export interface IAbstractUser { email: string; type: string; } -export interface IAttendee extends IAbstractUser { +export interface IPublicAttendee { avatar: string | null; badge_count: number; badges: IBadge[]; @@ -44,12 +53,15 @@ export interface IAttendee extends IAbstractUser { name: string; nickname: string; prizes: IPrize[]; - redeemables: IPrize[]; + redeemables: IRedeemable[]; token_balance: number; } +export interface IAttendee extends IAbstractUser, IPublicAttendee {} + export interface IStaff extends IAbstractUser { id: number; + is_admin: boolean; } export interface ISponsor extends IAbstractUser { diff --git a/context/Auth/index.ts b/context/Auth/index.ts index 7847d76f..29d2888c 100644 --- a/context/Auth/index.ts +++ b/context/Auth/index.ts @@ -1,7 +1,9 @@ export type { IBadge, IPrize, + IRedeemable, IAbstractUser, + IPublicAttendee, IAttendee, IStaff, ISponsor, diff --git a/context/Auth/withAuth.js b/context/Auth/withAuth.js index de0c587a..a080d389 100644 --- a/context/Auth/withAuth.js +++ b/context/Auth/withAuth.js @@ -43,6 +43,11 @@ export function withAuth(WrappedComponent) { "/staff/leaderboard", "/staff/cv", "/attendees/[uuid]", + ...(user.is_admin ? [ + "/staff/badgehistory", + "/staff/redeemhistory", + "/staff/spotlight", + ] : []) ].includes(router.pathname) ) { router.replace("/404"); @@ -62,23 +67,6 @@ export function withAuth(WrappedComponent) { return null; } break; - case USER.ROLES.ADMIN: - if ( - ![ - "/admin/badges", - "/admin/prizes", - "/admin/prizes/[uuid]", - "/admin/identifier", - "/admin/leaderboard", - "/admin/badgehistory", - "/admin/redeemhistory", - "/admin/spotlight", - "/attendees/[uuid]", - ].includes(router.pathname) - ) { - return router.replace("/404"); - } - break; } return ; diff --git a/layout/Admin/BadgeHistory/BadgeHistory.tsx b/layout/Admin/BadgeHistory/BadgeHistory.tsx index e3b4e912..da48fd21 100644 --- a/layout/Admin/BadgeHistory/BadgeHistory.tsx +++ b/layout/Admin/BadgeHistory/BadgeHistory.tsx @@ -1,11 +1,113 @@ -import { withAuth } from "@context/Auth"; +import AttendeeMention from "@components/AttendeeMention"; +import PaginatedSearch from "@components/PaginatedSearch"; +import Layout from "@components/Layout"; +import { IPublicAttendee, IBadge, IStaff, withAuth } from "@context/Auth"; +import { useEffect, useState } from "react"; +import Table, { TableColumn } from "@components/Table"; + +interface IBadgeAward { + attendee: IPublicAttendee; + badge: IBadge; + staff: IStaff; + timestamp: string; +} + +function displayTimestamp(timestamp: string) { + return timestamp; //TODO +} export function BadgeHistory() { - return ( -
+ const x = { + timestamp: "2023-02-16T15:34:27Z", + attendee: { + avatar: + "https://sei24-staging.s3.amazonaws.com/uploads/attendee/avatars/f7c90ab0-aa79-4528-bff3-3ad6a811ac83/original.png?v=63872645262", + badge_count: 13, + badges: [ + { + avatar: null, + begin: "2023-02-14T09:00:00Z", + description: "Visita o stand da IT Sector", + end: "2023-02-16T19:00:00Z", + id: 28, + name: "IT Sector", + tokens: 40, + type: 4, + }, + { + avatar: null, + begin: "2023-02-15T09:00:00Z", + description: "Visita o stand da Continental", + end: "2023-02-17T17:00:00Z", + id: 29, + name: "Continental", + tokens: 40, + type: 4, + }, + ], + course: 2, + cv: "https://sei24-staging.s3.amazonaws.com/uploads/attendee/cvs/f7c90ab0-aa79-4528-bff3-3ad6a811ac83/original.pdf?v=63872641922", + entries: 14, + id: "f7c90ab0-aa79-4528-bff3-3ad6a811ac83", + name: "attendee69", + nickname: "jmf", + prizes: [ + { + avatar: + "https://sei24-staging.s3.amazonaws.com/uploads/prize/avatars/14/original.png?v=63870223172", + id: 14, + is_redeemable: false, + name: "Nada", + not_redeemed: 1, + }, + ], + redeemables: [ + { + id: 18, + image: + "https://sei24-staging.s3.amazonaws.com/uploads/redeemable/avatars/18/original.png?v=63870223178", + name: "Caneca SEI '23", + not_redeemed: 1, + price: 0, + quantity: 1, + }, + ], + token_balance: 110, + }, + badge: { + avatar: null, + begin: "2023-02-14T09:00:00Z", + description: "Visita o stand da IT Sector", + end: "2023-02-16T19:00:00Z", + id: 28, + name: "IT Sector", + tokens: 40, + type: 4, + }, + staff: { + email: "staff13@seium.org", + id: 13, + type: "staff", + is_admin: false + }, + }; + const fetchAwards = () => [x,x,x,x,x]; -
) - ; + return ( + + fetchElems={fetchAwards} + showElems={(ee) => + elems={ee}> + header="Attendee" getter={(e) => } /> + header="Badge" getter={(e) => e.badge.name } /> + header="Staff" getter={(e) => e.staff.email } /> + header="Timestamp" getter={(e) => displayTimestamp(e.timestamp) } /> + + } + limit={50} + /> + + ); } -export default withAuth(BadgeHistory); +export default withAuth(BadgeHistory); \ No newline at end of file diff --git a/layout/Admin/RedeemHistory/RedeemHistory.tsx b/layout/Admin/RedeemHistory/RedeemHistory.tsx index 9954e89a..47840589 100644 --- a/layout/Admin/RedeemHistory/RedeemHistory.tsx +++ b/layout/Admin/RedeemHistory/RedeemHistory.tsx @@ -1,7 +1,219 @@ -import { withAuth } from "@context/Auth"; +import AttendeeMention from "@components/AttendeeMention"; +import PaginatedSearch from "@components/PaginatedSearch"; +import Layout from "@components/Layout"; +import { IPublicAttendee, IPrize, IStaff, withAuth, IRedeemable, IAttendee } from "@context/Auth"; +import { useEffect, useState } from "react"; +import Table, { TableColumn } from "@components/Table"; + +interface IRedeem { + attendee: IPublicAttendee; + redeemable?: IRedeemable; + prize?: IPrize; + quantity: number; + timestamp: string; +} + +enum RedeemType { + Prize, + Redeemable +} + +interface IRedeemDetails { + attendee: IPublicAttendee; + type: RedeemType; + name: string; + image: string; + quantity: string; + timestamp: string; +} + +function redeemDetails(redeem : IRedeem) : IRedeemDetails { + if (redeem.prize) { + return { + attendee: redeem.attendee, + type: RedeemType.Prize, + name: redeem.prize.name, + image: redeem.prize.avatar, + quantity: redeem.quantity.toString(), + timestamp: redeem.timestamp + } + } else { + return { + attendee: redeem.attendee, + type: RedeemType.Redeemable, + name: redeem.redeemable.name, + image: redeem.redeemable.image, + quantity: "-", + timestamp: redeem.timestamp + } + } +} + +function displayRedeemType(type: RedeemType) { + switch (type) { + case RedeemType.Prize: return "Prize"; + case RedeemType.Redeemable: return "Shop Item" + } +} + +function displayTimestamp(timestamp: string) { + return timestamp; //TODO +} export function RedeemHistory() { - return
; + const x: IRedeem = { + timestamp: "2023-02-16T15:34:27Z", + attendee: { + avatar: + "https://sei24-staging.s3.amazonaws.com/uploads/attendee/avatars/f7c90ab0-aa79-4528-bff3-3ad6a811ac83/original.png?v=63872645262", + badge_count: 13, + badges: [ + { + avatar: null, + begin: "2023-02-14T09:00:00Z", + description: "Visita o stand da IT Sector", + end: "2023-02-16T19:00:00Z", + id: 28, + name: "IT Sector", + tokens: 40, + type: 4, + }, + { + avatar: null, + begin: "2023-02-15T09:00:00Z", + description: "Visita o stand da Continental", + end: "2023-02-17T17:00:00Z", + id: 29, + name: "Continental", + tokens: 40, + type: 4, + }, + ], + course: 2, + cv: "https://sei24-staging.s3.amazonaws.com/uploads/attendee/cvs/f7c90ab0-aa79-4528-bff3-3ad6a811ac83/original.pdf?v=63872641922", + entries: 14, + id: "f7c90ab0-aa79-4528-bff3-3ad6a811ac83", + name: "attendee69", + nickname: "jmf", + prizes: [ + { + avatar: + "https://sei24-staging.s3.amazonaws.com/uploads/prize/avatars/14/original.png?v=63870223172", + id: 14, + is_redeemable: false, + name: "Nada", + not_redeemed: 1, + }, + ], + redeemables: [ + { + id: 18, + image: + "https://sei24-staging.s3.amazonaws.com/uploads/redeemable/avatars/18/original.png?v=63870223178", + name: "Caneca SEI '23", + not_redeemed: 1, + price: 0, + quantity: 1, + }, + ], + token_balance: 110, + }, + redeemable: { + id: 18, + image: + "https://sei24-staging.s3.amazonaws.com/uploads/redeemable/avatars/18/original.png?v=63870223178", + name: "Caneca SEI '23", + not_redeemed: 1, + price: 0, + quantity: 1, + }, + quantity: 1 + }; + const y:IRedeem = { + timestamp: "2023-02-16T15:34:27Z", + attendee: { + avatar: + "https://sei24-staging.s3.amazonaws.com/uploads/attendee/avatars/f7c90ab0-aa79-4528-bff3-3ad6a811ac83/original.png?v=63872645262", + badge_count: 13, + badges: [ + { + avatar: null, + begin: "2023-02-14T09:00:00Z", + description: "Visita o stand da IT Sector", + end: "2023-02-16T19:00:00Z", + id: 28, + name: "IT Sector", + tokens: 40, + type: 4, + }, + { + avatar: null, + begin: "2023-02-15T09:00:00Z", + description: "Visita o stand da Continental", + end: "2023-02-17T17:00:00Z", + id: 29, + name: "Continental", + tokens: 40, + type: 4, + }, + ], + course: 2, + cv: "https://sei24-staging.s3.amazonaws.com/uploads/attendee/cvs/f7c90ab0-aa79-4528-bff3-3ad6a811ac83/original.pdf?v=63872641922", + entries: 14, + id: "f7c90ab0-aa79-4528-bff3-3ad6a811ac83", + name: "attendee69", + nickname: "jmf", + prizes: [ + { + avatar: + "https://sei24-staging.s3.amazonaws.com/uploads/prize/avatars/14/original.png?v=63870223172", + id: 14, + is_redeemable: false, + name: "Nada", + not_redeemed: 1, + }, + ], + redeemables: [ + { + id: 18, + image: + "https://sei24-staging.s3.amazonaws.com/uploads/redeemable/avatars/18/original.png?v=63870223178", + name: "Caneca SEI '23", + not_redeemed: 1, + price: 0, + quantity: 1, + }, + ], + token_balance: 110, + }, + prize: { + avatar: + "https://sei24-staging.s3.amazonaws.com/uploads/prize/avatars/14/original.png?v=63870223172", + id: 69, + is_redeemable: true, + name: "Air Fryer", + not_redeemed: 1, + }, + quantity: 1 + }; + const fetchRedeems = (query:string , offset:number, limit:number) => [x,y,x,x,y]; + + return ( + + fetchElems={fetchRedeems} + showElems={(ee) => + elems={ee.map(redeemDetails)}> + header="Attendee" getter={(e) => } /> + header="Type" getter={(e) => displayRedeemType(e.type) } /> + header="Redeem" getter={(e) => e.name } /> + header="Quantity" getter={(e) => e.quantity } /> + header="Timestamp" getter={(e) => e.timestamp } /> + + } + limit={50} + /> + + ); } export default withAuth(RedeemHistory); diff --git a/layout/Admin/Spotlight/Spotlight.tsx b/layout/Admin/Spotlight/Spotlight.tsx index dadb964b..b88cf7d2 100644 --- a/layout/Admin/Spotlight/Spotlight.tsx +++ b/layout/Admin/Spotlight/Spotlight.tsx @@ -1,5 +1,12 @@ +import Layout from "@components/Layout"; import { withAuth } from "@context/Auth"; -export function Spotlight() {} +export function Spotlight() { + return ( + + TODO + + ); +} export default withAuth(Spotlight); diff --git a/layout/Login/Login.tsx b/layout/Login/Login.tsx index 012ef8f8..3db95e7e 100644 --- a/layout/Login/Login.tsx +++ b/layout/Login/Login.tsx @@ -14,15 +14,29 @@ import Title from "@layout/moonstone/authentication/Title"; import Text from "@layout/moonstone/authentication/Text"; import { LoginForm } from "./components"; +function decodePathname(from : string | string[]) { + if (from) { + const pathname : string = typeof from === "string" ? from : from[0]; + try { + return decodeURIComponent(pathname as string).replace(/\[|\]/i,""); + } catch (e) { + if (!(e instanceof URIError)) { + throw e; + } + } + } + + return "/"; +} + function Login() { const router = useRouter(); const { user } = useAuth(); if (user) { - router.replace( - (router.query.from && decodeURIComponent(router.query.from as string)) ?? - "/" - ); + router.replace({ + pathname: decodePathname(router.query.from) + }); return null; } diff --git a/lib/user.js b/lib/user.js index dd8c172c..62eb6dd5 100644 --- a/lib/user.js +++ b/lib/user.js @@ -2,5 +2,4 @@ export const ROLES = { ATTENDEE: "attendee", SPONSOR: "company", STAFF: "staff", - ADMIN: "admin", }; diff --git a/pages/admin/badges.tsx b/pages/admin/badges.tsx deleted file mode 100644 index e5215fda..00000000 --- a/pages/admin/badges.tsx +++ /dev/null @@ -1 +0,0 @@ -export { default } from "@layout/Staff/Badges"; diff --git a/pages/admin/identifier.tsx b/pages/admin/identifier.tsx deleted file mode 100644 index c8b65c7e..00000000 --- a/pages/admin/identifier.tsx +++ /dev/null @@ -1 +0,0 @@ -export { default } from "@layout/Staff/Identifier"; diff --git a/pages/admin/leaderboard.tsx b/pages/admin/leaderboard.tsx deleted file mode 100644 index e25d4484..00000000 --- a/pages/admin/leaderboard.tsx +++ /dev/null @@ -1 +0,0 @@ -export { default } from "@layout/shared/Leaderboard"; diff --git a/pages/admin/prizes/[uuid].tsx b/pages/admin/prizes/[uuid].tsx deleted file mode 100644 index 268f4944..00000000 --- a/pages/admin/prizes/[uuid].tsx +++ /dev/null @@ -1 +0,0 @@ -export { default } from "@layout/Staff/Prize"; diff --git a/pages/admin/prizes/index.tsx b/pages/admin/prizes/index.tsx deleted file mode 100644 index dab117c2..00000000 --- a/pages/admin/prizes/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { default } from "@layout/Staff/Prizes"; diff --git a/pages/admin/badgehistory.tsx b/pages/staff/badgehistory.tsx similarity index 100% rename from pages/admin/badgehistory.tsx rename to pages/staff/badgehistory.tsx diff --git a/pages/admin/redeemhistory.tsx b/pages/staff/redeemhistory.tsx similarity index 100% rename from pages/admin/redeemhistory.tsx rename to pages/staff/redeemhistory.tsx diff --git a/pages/admin/spotlight.tsx b/pages/staff/spotlight.tsx similarity index 100% rename from pages/admin/spotlight.tsx rename to pages/staff/spotlight.tsx diff --git a/tailwind.config.js b/tailwind.config.js index bcb0fffd..c8cbbcc9 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,11 +1,32 @@ const defaultTheme = require("tailwindcss/defaultTheme"); +function fromTo(start, end) { + return Array.from({length: end-start+1}, (_, i) => i + start) +} + +function concatFlatMap(first, ...args) { + var ans = Array.isArray(first) ? first.map((i) => i.toString()) : [first.toString()]; + for (a in args) { + if (Array.isArray(a)) { + ans = ans.flatMap((i) => ans.map((s) => s + i.toString()), a); + } else { + ans.map((s) => s + a.toString()); + } + } + return ans; +} + module.exports = { content: [ "./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}", "./layout/**/*.{js,ts,jsx,tsx}", ], + safelist: [ + ...concatFlatMap(["grid-cols-", "col-span-"], fromTo(1,12)), + ...concatFlatMap(["px-", "pl-"], [0,0.5,1,1.5,2,2.5,3,3.5,4,5,6,7,8,9,10,11,12,14,16,20,24,28,32,36,40]), + ...concatFlatMap(["bg-", "text-"], ["primary", "secondary", "tertiary", "quaternary", "quinary", "white"]), + ], theme: { screens: { xs: "360px", From 82865d11b90b51e28a77427901c279fedd356f42 Mon Sep 17 00:00:00 2001 From: tiago-bacelar Date: Sat, 3 Feb 2024 21:17:47 +0000 Subject: [PATCH 4/5] Refactored all tables into the new Table component --- components/Table/TableColumn.tsx | 8 +- components/Table/index.tsx | 4 +- layout/Attendee/Wheel/Wheel.tsx | 84 +++++++++---------- .../Wheel/components/ListItem3Cols/index.tsx | 36 -------- .../Wheel/components/ListItem4Cols/index.tsx | 29 ------- layout/Attendee/Wheel/components/index.ts | 4 +- layout/shared/Leaderboard/Leaderboard.tsx | 31 ++++++- .../Leaderboard/components/Table/index.tsx | 79 ----------------- layout/shared/Leaderboard/components/index.ts | 3 - tailwind.config.js | 16 ++-- 10 files changed, 82 insertions(+), 212 deletions(-) delete mode 100644 layout/Attendee/Wheel/components/ListItem3Cols/index.tsx delete mode 100644 layout/Attendee/Wheel/components/ListItem4Cols/index.tsx delete mode 100644 layout/shared/Leaderboard/components/Table/index.tsx delete mode 100644 layout/shared/Leaderboard/components/index.ts diff --git a/components/Table/TableColumn.tsx b/components/Table/TableColumn.tsx index 08d4499d..05625097 100644 --- a/components/Table/TableColumn.tsx +++ b/components/Table/TableColumn.tsx @@ -8,9 +8,9 @@ export enum Align { export function alignToCSS(align: Align): string { switch (+align) { - case Align.Left: return "text-left justify-self-left"; - case Align.Center: return "text-center justify-self-center"; - case Align.Right: return "text-right justify-self-right"; + case Align.Left: return "text-left justify-start"; + case Align.Center: return "text-center justify-center"; + case Align.Right: return "text-right justify-end"; } } @@ -49,7 +49,7 @@ export function resolveProps(props: TableColumnProps): ResolvedTableColumn export function printHeader(props : TableColumnProps) { return ( -
+
{ props.header }
); diff --git a/components/Table/index.tsx b/components/Table/index.tsx index 55622b04..0457d84e 100644 --- a/components/Table/index.tsx +++ b/components/Table/index.tsx @@ -15,12 +15,12 @@ export default function Table({ children, elems, elemKey } : TableProps) { const anyHasHeader = cols.reduce((or, c) => c.header || or, false); const printElem = (elem: T, isFirst: boolean, isLast: boolean) => { - const border = isLast ? "" : "boprder-b-solid border-b-2"; + const border = isLast ? "" : "border-b-2"; const key = elemKey ? elemKey(elem) : Math.random(); return
{ cols.map((c) => -
+
{ c.getter(elem) }
) } diff --git a/layout/Attendee/Wheel/Wheel.tsx b/layout/Attendee/Wheel/Wheel.tsx index 9f49d12b..7b306118 100644 --- a/layout/Attendee/Wheel/Wheel.tsx +++ b/layout/Attendee/Wheel/Wheel.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect, ReactNode } from "react"; import { withAuth, useAuth, IAttendee } from "@context/Auth"; import Heading from "@components/Heading"; @@ -6,8 +6,6 @@ import Button from "@components/Button"; import Layout from "@components/Layout"; import { - ListItem3Cols, - ListItem4Cols, WheelComponent, WheelMessage, } from "./components"; @@ -20,6 +18,23 @@ import { getWheelLatestWins, spinWheel, } from "@lib/api"; +import Table, { Align, TableColumn } from "@components/Table"; +import AttendeeMention from "@components/AttendeeMention"; + +interface IPrize { + avatar: string; + id: number; + max_amount_per_attendee: number; + name: string; + stock: number; +} + +interface IWin { + attendee_name: string; + attendee_nickname: string; + date: string; + prize: IPrize; +} /* Gets how long ago the given date/time was in a user friendly way (10 seconds ago, 1 minute ago, etc) @@ -49,6 +64,10 @@ function displayTimeSince(dateString) { return value < 0 ? "Now" : string; } +function displayQuantity(qt) { + return qt > 1000 ? "∞" : qt; +} + function WheelPage() { const defaultState = { angle: 0, @@ -62,12 +81,12 @@ function WheelPage() { refetchUser: () => void; }; - const [prizes, updatePrizes] = useState([]); - const [price, updatePrice] = useState(null); - const [latestWins, updateLatestWins] = useState([]); - const [error, updateError] = useState(false); - const [wheelMessage, updateWheelMessage] = useState(<>); - const [isSpinning, setIsSpinning] = useState(false); + const [prizes, updatePrizes] = useState([]); + const [price, updatePrice] = useState(null); + const [latestWins, updateLatestWins] = useState([]); + const [error, updateError] = useState(false); + const [wheelMessage, updateWheelMessage] = useState(<>); + const [isSpinning, setIsSpinning] = useState(false); const requestAllInfo = () => { getWheelPrizes() @@ -166,25 +185,6 @@ function WheelPage() { if (st.speed > 0) setTimeout(changeState, 1000 / 60); }, [st]); - const prizeComponents = prizes.map((entry, id) => ( - - )); - const latestWinsComponents = latestWins.map((entry, id) => ( - - )); return (
@@ -224,26 +224,22 @@ function WheelPage() {
-
{latestWinsComponents}
+ + elemAlign={Align.Left} getter={(w) => } /> + elemAlign={Align.Right} getter={(w) => } /> + elemAlign={Align.Left} colSpan={2} getter={(w) =>

{w.prize.name}

} /> + elemAlign={Align.Right} elemPadding={4} getter={(w) =>

{displayTimeSince(w.date)}

} /> +
-
-
-

Image

-
-
-

Name

-
-
-

Qt/pax

-
-
-

Qt

-
-
- {prizeComponents} + + header="Image" getter={(p) => } /> + header="Name" colSpan={3} headerAlign={Align.Left} elemAlign={Align.Left} getter={(p) => p.name } /> + header="Qt/pax" getter={(p) => displayQuantity(p.max_amount_per_attendee) } /> + header="Qt" getter={(p) => displayQuantity(p.stock) } /> +
diff --git a/layout/Attendee/Wheel/components/ListItem3Cols/index.tsx b/layout/Attendee/Wheel/components/ListItem3Cols/index.tsx deleted file mode 100644 index 96759f71..00000000 --- a/layout/Attendee/Wheel/components/ListItem3Cols/index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import Link from "next/link"; - -export default function ListItem3Cols({ - user_name, - user_nickname, - prize, - when, - isLast = false, - isFullBold = false, -}) { - if (!isLast) { - var border = "border-b-solid border-b-2"; - } - if (isFullBold) { - } - return ( -
-
- -

{user_name}

- -
-
- -
-
-

{prize.name}

-
-
-

- {when} -

-
-
- ); -} diff --git a/layout/Attendee/Wheel/components/ListItem4Cols/index.tsx b/layout/Attendee/Wheel/components/ListItem4Cols/index.tsx deleted file mode 100644 index 562aa040..00000000 --- a/layout/Attendee/Wheel/components/ListItem4Cols/index.tsx +++ /dev/null @@ -1,29 +0,0 @@ -export default function ListItem4Cols({ - name, - maxQuantity, - quantity, - img, - isLast = false, -}) { - if (!isLast) { - var border = "border-b-solid border-b-2"; - } - return ( -
-
- -
-
-

{name}

-
-
-

- {maxQuantity > 1000 ? "∞" : maxQuantity} -

-
-
-

{quantity > 1000 ? "∞" : quantity}

-
-
- ); -} diff --git a/layout/Attendee/Wheel/components/index.ts b/layout/Attendee/Wheel/components/index.ts index eec93c9c..b06398f2 100644 --- a/layout/Attendee/Wheel/components/index.ts +++ b/layout/Attendee/Wheel/components/index.ts @@ -1,6 +1,4 @@ -import ListItem3Cols from "./ListItem3Cols"; -import ListItem4Cols from "./ListItem4Cols"; import WheelComponent from "./WheelComponent"; import WheelMessage from "./WheelMessage"; -export { ListItem3Cols, ListItem4Cols, WheelComponent, WheelMessage }; +export { WheelComponent, WheelMessage }; diff --git a/layout/shared/Leaderboard/Leaderboard.tsx b/layout/shared/Leaderboard/Leaderboard.tsx index 03f0d747..9c9b74ec 100644 --- a/layout/shared/Leaderboard/Leaderboard.tsx +++ b/layout/shared/Leaderboard/Leaderboard.tsx @@ -1,9 +1,8 @@ import { useState, useEffect } from "react"; -import { withAuth, useAuth } from "@context/Auth"; +import { withAuth, useAuth, IPublicAttendee } from "@context/Auth"; import Layout from "@components/Layout"; -import { Table } from "./components"; import Button from "@components/Button"; import Day from "@components/Schedule/Day"; @@ -13,6 +12,8 @@ import { getLeaderboard } from "@lib/api"; import scheduleData from "@data/schedule.json"; import dayjs from "dayjs"; +import Table, { Align, TableColumn } from "@components/Table"; +import AttendeeMention from "@components/AttendeeMention"; function leapYear(year) { return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; @@ -74,6 +75,16 @@ function addDate(date, days) { return year + "/" + month + "/" + day; } +interface IToShow { + index: number; + id: string; + avatar: string | null; + name: string; + nickname: string; + badges: number; + token_balance: number; +} + function Leaderboard() { /* Fetch first and last day of the event from schedule data */ const eventDates = scheduleData.map((day) => day.date).sort(); @@ -98,7 +109,7 @@ function Leaderboard() { const requestLeaderboard = () => { const args = hallOfFame ? "" : date.replaceAll("/", "-"); getLeaderboard(args) - .then((response) => updateLeaderboard(response.data)) + .then((response) => updateLeaderboard(response.data.map((l,i) => ({index: i, ...l})))) .catch((_) => updateError(true)); }; @@ -114,6 +125,13 @@ function Leaderboard() { updateDate(new_date); }; + const maxUsers = 5; + const userIndex = leaderboard.findIndex((l) => l.id === user.id); + const toShow: IToShow[] = leaderboard.slice(0, Math.min(maxUsers, leaderboard.length)).concat(userIndex >= maxUsers ? [leaderboard[userIndex]] : []); + console.log(toShow); + console.log(userIndex); + + return (
{error && } - + +
+ header="Rank" headerAlign={Align.Left} elemAlign={Align.Left} elemPadding={3} getter={(e) => e.index + 1 }/> + header="Name" headerAlign={Align.Center} elemAlign={Align.Center} getter={(e) => }/> + header="Badges" headerAlign={Align.Right} elemAlign={Align.Right} elemPadding={5} getter={(e) => e.badges }/> +
diff --git a/layout/shared/Leaderboard/components/Table/index.tsx b/layout/shared/Leaderboard/components/Table/index.tsx deleted file mode 100644 index 29ef8f17..00000000 --- a/layout/shared/Leaderboard/components/Table/index.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import Link from "next/link"; - -export default function Table({ list, user, maxUsersToShow }) { - const toShow = list.slice(0, Math.min(maxUsersToShow, list.length)); - - const rows = toShow.map((entry, id) => ( -
-
- {id + 1} -
-
- {entry.name} -
-
- {entry.badges} -
-
- )); - - let userId = 0; - let userBadges = 0; - let userName = ""; - for (var i = 0; i < list.length; i++) { - if (list[i].id == user) { - userId = i; - userName = list[i].name; - userBadges = list[i].badges; - break; - } - } - - let extraUser = - userId < maxUsersToShow ? ( - <> - ) : ( -
-
- {userId + 1} -
-
- {userName} -
-
- {userBadges} -
-
- ); - - return ( -
-
-
- Rank -
-
- Name -
-
- Badges -
-
- - {rows} - {extraUser} -
- ); -} diff --git a/layout/shared/Leaderboard/components/index.ts b/layout/shared/Leaderboard/components/index.ts deleted file mode 100644 index 0084055d..00000000 --- a/layout/shared/Leaderboard/components/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Table from "./Table"; - -export { Table }; diff --git a/tailwind.config.js b/tailwind.config.js index c8cbbcc9..ae39d22d 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -5,14 +5,14 @@ function fromTo(start, end) { } function concatFlatMap(first, ...args) { - var ans = Array.isArray(first) ? first.map((i) => i.toString()) : [first.toString()]; - for (a in args) { - if (Array.isArray(a)) { - ans = ans.flatMap((i) => ans.map((s) => s + i.toString()), a); - } else { - ans.map((s) => s + a.toString()); - } - } + const list = Array.isArray(first) ? first : [first]; + var ans = list.map((i) => i.toString()); + + args.forEach((a) => { + const list = Array.isArray(a) ? a : [a]; + ans = list.flatMap((i) => ans.map((s) => s + i.toString())); + }); + return ans; } From 0b967295866ddb3caaf5bbabecae019a9bb13899 Mon Sep 17 00:00:00 2001 From: tiago-bacelar Date: Sat, 3 Feb 2024 21:24:17 +0000 Subject: [PATCH 5/5] Added env variable to qr canvas --- components/QRCodeCanvas/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/QRCodeCanvas/index.tsx b/components/QRCodeCanvas/index.tsx index ade0a2d3..f6b76518 100644 --- a/components/QRCodeCanvas/index.tsx +++ b/components/QRCodeCanvas/index.tsx @@ -10,7 +10,7 @@ const Canvas = ({ uuid }: IProps) => { const [src, setSrc] = useState(""); useEffect(() => { - QRCode.toDataURL(`https://seium.org/attendees/${uuid}`).then(setSrc); //TODO: env var + QRCode.toDataURL(`https://${process.env.NEXT_PUBLIC_QRCODE_HOST}/attendees/${uuid}`).then(setSrc); }, [uuid]); return qrcode;