Skip to content

Commit

Permalink
more progress
Browse files Browse the repository at this point in the history
  • Loading branch information
WhiteHoodHacker committed Sep 21, 2023
1 parent 03ab8b7 commit f381d54
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 76 deletions.
170 changes: 148 additions & 22 deletions src/app/(new-signage)/scoreboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,155 @@
"use client";

import { useEffect, useState } from "react";
import { motion, useAnimate } from "framer-motion";
import Countdown from "@/components/Countdown";
import LogoReveal from "@/components/LogoReveal";
import Scoreboard from "@/components/Scoreboard";

export default function ScoreboardPage() {
async function seqFromRight(scope: any, animate: any, delay: number = 0) {
await animate(scope.current, {
translateX: "2rem",
}, {
duration: 0.0
});
await new Promise(resolve => setTimeout(resolve, delay));
await animate(scope.current, {
translateX: "0%",
}, {
duration: 0.2
});
}

async function seqFadeIn(scope: any, animate: any, delay: number = 0) {
await animate(scope.current, {
opacity: 0,
}, {
duration: 0.0
});
await new Promise(resolve => setTimeout(resolve, delay));
await animate(scope.current, {
opacity: 1,
}, {
duration: 0.2
});
}

function ScoreboardPage() {
return (
<div className="grid grid-cols-2 gap-2 p-2">
<div className="flex flex-col">
<span className="font-medium text-xl">
Advanced Division
</span>
<Scoreboard
ctfd_url="http://signage-api.sigpwny.com/"
matched={true}
limit={5}
/>
<>
<Scoreboard ctfd_url="https://signage-api.sigpwny.com" limit={5} />
</>
);
}

export default function SignagePage() {
const time_start = "2023-09-23T12:00:00-05:00"
const time_close = "2023-09-23T18:00:00-05:00"
const [loaded, setLoaded] = useState(false);
const [scopeHeader, animateScopeHeader] = useAnimate();
const [scopeTimer, animateScopeTimer] = useAnimate();
const [scopeBody, animateScopeBody] = useAnimate();
const [scopeDetails, animateScopeDetails] = useAnimate();
useEffect(() => {
setTimeout(() => {
setLoaded(true);
seqFromRight(scopeHeader, animateScopeHeader, 300);
seqFadeIn(scopeHeader, animateScopeHeader, 300);
seqFadeIn(scopeBody, animateScopeBody, 500);
seqFadeIn(scopeTimer, animateScopeTimer, 1000);
seqFadeIn(scopeDetails, animateScopeDetails, 1000);
}, 4000);
// Automatically refresh the page
setTimeout(() => {
location.reload();
}, 1000 * 120);
}, []);

return (
<motion.div
layout="position"
className="m-auto w-[92vw] h-[88vh] flex flex-col grow-1 shrink-0 relative"
>
<div className="flex flex-row justify-between">
<div className="flex flex-col">
<motion.div
layout="position"
className="opacity-0 h-fit w-fit"
ref={scopeHeader}
>
<p className="font-medium text-xl leading-none">
SIGPwny Presents
</p>
</motion.div>
<motion.div
layout="position"
className={`flex h-fit ${loaded ? "" : "absolute inset-0 m-auto"}`}
variants={{
initial: {
width: "60vw",
},
animate: {
width: "50vw",
}
}}
initial="initial"
animate={(loaded ? "animate" : "initial")}
transition={{
duration: 0.3,
ease: "easeInOut",
}}
>
<LogoReveal />
</motion.div>
</div>
<motion.div
layout="position"
className="opacity-0 font-medium"
ref={scopeTimer}
>
<Countdown time_start={time_start} time_close={time_close} />
</motion.div>
</div>
<div className="flex flex-col">
<span className="font-medium text-xl">
Beginner Division
<motion.div
layout="position"
className="opacity-0 flex flex-row flex-grow-1 flex-shrink-0 mt-8 grainger:mt-2 items-center"
ref={scopeBody}
>
<div className="flex flex-col">
<p className="text-4xl grainger:text-3xl mb-4 leading-none font-medium">
A beginner-friendly<br /> <span className="text-yellow-dark">hacking</span> competition.
</p>
<p className="text-xl leading-none">
Register at <span className="text-yellow-dark font-medium">sigpwny.com/fallctf</span>.
</p>
</div>
<div className="flex flex-col ml-auto">
<img
src="/assets/qr-fallctf.png"
className="pointer-events-none h-[400px] grainger:h-[350px] w-auto"
alt="https://sigpwny.com/fallctf"
/>
</div>
</motion.div>
<motion.div
layout="position"
className="opacity-0 mt-auto flex flex-col"
ref={scopeDetails}
>
<span className="grainger:hidden">
<p className="text-xl font-medium">
September 23rd, 12–6 PM
</p>
<p className="text-xl leading-none font-medium">
CIF 3039
</p>
</span>
<Scoreboard
ctfd_url="http://signage-api.sigpwny.com/"
matched={false}
limit={5}
/>
</div>
</div>
<span className="hidden grainger:flex">
<p className="text-xl leading-none font-medium">
September 23rd, 12–6 PM @ CIF 3039
</p>
</span>
</motion.div>
</motion.div>
);
}
};
36 changes: 18 additions & 18 deletions src/components/Countdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,63 +70,63 @@ export default function Countdown({ time_start, time_close }: Props) {
return (
<>
{timer.status !== TimerStatus.UNINITIALIZED && timer.status !== TimerStatus.AFTER_CLOSE ? (
<div className="flex flex-col">
<motion.p layout="position" className="text-[3rem] leading-none">
<div className="flex flex-col leading-none">
<motion.span layout="position" className="leading-none">
{timer.status === TimerStatus.BEFORE_START ?
"Starting in" : timer.status === TimerStatus.DURING ?
"Ending in" :
"Ended"
}
</motion.p>
</motion.span>
<div className="flex flex-row">
{timer.days > 0 ? (
<>
<div className="flex flex-col">
<div className="text-[7rem] font-bold">
<div className="text-4xl font-bold">
{formatNumber(timer.days)}
</div>
<p>
<span>
{timer.days === 1 ? "Day" : "Days"}
</p>
</span>
</div>
<span className="text-[7rem] font-bold">
<span className="text-4xl font-bold">
:
</span>
</>
) : null}
{timer.days > 0 || timer.hours > 0 ? (
<>
<div className="flex flex-col">
<div className="text-[7rem] font-bold">
<div className="text-4xl font-bold">
{formatNumber(timer.hours)}
</div>
<p>
<span>
{timer.hours === 1 ? "Hour" : "Hours"}
</p>
</span>
</div>
<span className="text-[7rem] font-bold">
<span className="text-4xl font-bold">
:
</span>
</>
) : null}
<div className="flex flex-col">
<div className="text-[7rem] font-bold">
<div className="text-4xl font-bold">
{formatNumber(timer.minutes)}
</div>
<p>
<span>
{timer.minutes === 1 ? "Min" : "Mins"}
</p>
</span>
</div>
<span className="text-[7rem] font-bold">
<span className="text-4xl font-bold">
:
</span>
<div className="flex flex-col">
<div className="text-[7rem] font-bold">
<div className="text-4xl font-bold">
{formatNumber(timer.seconds)}
</div>
<p>
<span>
{timer.seconds === 1 ? "Sec" : "Secs"}
</p>
</span>
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/LogoReveal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default function LogoReveal() {
<motion.svg
viewBox="0 0 1024 150"
display="block"
fontSize="10rem"
fontSize="3.3rem"
fontWeight="bold"
>
<linearGradient
Expand Down
99 changes: 64 additions & 35 deletions src/components/Scoreboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,50 @@ interface CTFdScoreboardUserTeam {

interface ScoreboardProps {
ctfd_url: string;
matched: boolean;
limit?: number;
}

export default function Scoreboard(data: ScoreboardProps) {
const { ctfd_url, matched, limit } = data;
interface ScoreboardTableProps {
scoreboard: CTFdScoreboardUserTeam[];
limit?: number;
}

export function ScoreboardTable(props: ScoreboardTableProps) {
const { scoreboard, limit } = props;
const display_limit = limit === undefined ? scoreboard.length : limit;
return (
<div className="flex flex-col gap-1 w-full">
{scoreboard.length != 0 ? (
<>
{scoreboard.filter((user, idx) => idx < display_limit).map((user, idx) => (
<div key={idx} className="flex flex-row bg-surface-100 border-surface-150 border-2 rounded-lg p-1">
<span className="flex flex-col basis-1/12 font-mono">
{idx + 1}
</span>
<span className="flex flex-col basis-9/12 font-mono overflow-hidden">
<span className="whitespace-nowrap overflow-hidden text-ellipsis min-w-0">
{user.name}
</span>
</span>
<span className="flex flex-col basis-2/12 font-mono">
{user.score}
</span>
</div>
))}
</>
) : (
<div className="flex flex-row">
<span className="font-medium leading-none">
Scoreboard is currently hidden
</span>
</div>
)}
</div>
);
}

export default function Scoreboard(props: ScoreboardProps) {
const { ctfd_url, limit } = props;
// Test data
const test_scoreboard = [
{
Expand Down Expand Up @@ -69,7 +107,8 @@ export default function Scoreboard(data: ScoreboardProps) {
pos: 7,
}
];
const [scoreboard, setScoreboard] = useState<CTFdScoreboardUserTeam[]>(test_scoreboard);
const [matched_scoreboard, setMatchedScoreboard] = useState<CTFdScoreboardUserTeam[]>(test_scoreboard);
const [unmatched_scoreboard, setUnmatchedScoreboard] = useState<CTFdScoreboardUserTeam[]>(test_scoreboard);
// Initialize split scoreboard from the CTFd API
useEffect(() => {
async function fetchScoreboard() {
Expand All @@ -79,41 +118,31 @@ export default function Scoreboard(data: ScoreboardProps) {
const data = await response.json();
if (!data.success || !data.data || !data.data.matched || !data.data.unmatched) return;
console.log(data.data)
// Get first 5 users from the matched or unmatched scoreboard, or all if limit is not set
// const scoreboard = matched ? data.data.matched : data.data.unmatched;
// const scoreboard_limit = limit ? limit : scoreboard.length;
// setScoreboard(scoreboard.slice(0, scoreboard_limit));
setMatchedScoreboard(data.data.matched as CTFdScoreboardUserTeam[]);
setUnmatchedScoreboard(data.data.unmatched as CTFdScoreboardUserTeam[]);
}
// fetchScoreboard();
}, []);
const display_limit = limit ? limit : scoreboard.length;
return (
<div className="flex flex-col gap-1 w-full">
{scoreboard.length != 0 ? (
<>
{scoreboard.filter((user, idx) => idx < display_limit).map((user, idx) => (
<div key={idx} className="flex flex-row bg-surface-100 border-surface-150 border-2 rounded-lg p-1">
<span className="flex flex-col basis-1/12 font-mono">
{idx + 1}
</span>
<span className="flex flex-col basis-9/12 font-mono overflow-hidden">
<span className="whitespace-nowrap overflow-hidden text-ellipsis min-w-0">
{user.name}
</span>
</span>
<span className="flex flex-col basis-2/12 font-mono">
{user.score}
</span>
</div>
))}
</>
) : (
<div className="flex flex-row">
<span className="font-medium leading-none">
Scoreboard is currently hidden
</span>
</div>
)}
<div className="grid grid-cols-2 gap-2 p-2">
<div className="flex flex-col">
<span className="font-medium text-xl">
Advanced Division
</span>
<ScoreboardTable
scoreboard={matched_scoreboard}
limit={limit}
/>
</div>
<div className="flex flex-col">
<span className="font-medium text-xl">
Beginner Division
</span>
<ScoreboardTable
scoreboard={unmatched_scoreboard}
limit={limit}
/>
</div>
</div>
);
}

0 comments on commit f381d54

Please sign in to comment.