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

Event Display Screen #440

Closed
wants to merge 7 commits into from
Closed
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
20 changes: 19 additions & 1 deletion components/Organizer/EventsTab/EventsTab.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
import { Button, Input, InputRef, Modal, notification, Table, InputNumber } from 'antd';
import { ColumnsType } from 'antd/es/table';
import { useEffect, useRef, useState } from 'react';
import { useContext, useEffect, useRef, useState } from 'react';
import { EventCountData, EventData } from '../../../types/database';
import { RequestType, useCustomSWR } from '../../../utils/request-utils';
import { mutate } from 'swr';
import { ObjectId } from 'mongoose';
import { handleSubmitFailure, handleSubmitSuccess } from '../../../lib/helpers';
import { useRouter } from 'next/router';
import { ThemeContext, getAccentColor, getThemedClass } from '../../../theme/themeProvider';
import styles from '../../../styles/Organizer.module.css';

interface EventDisplay extends EventData {
setCurEvent: (open: EventDisplay) => void;
}

export default function Events() {
const router = useRouter();
const [curEvent, setCurEvent] = useState<EventDisplay | null>(null);
const [events, setEvents] = useState<EventDisplay[]>([]);
const [nfcId, setNfcId] = useState<string>('');
const [loading, setLoading] = useState(false);
const [showSaveButton, setShowSaveButton] = useState(false);

const { accentColor, baseTheme } = useContext(ThemeContext);

const redirectToEventPage = () => {
router.push('/event');
};

const columns: ColumnsType<EventDisplay> = [
{
title: 'Day',
Expand Down Expand Up @@ -240,6 +250,14 @@ export default function Events() {
Save Changes
</Button>
)}
<div>
<button
className={styles[getThemedClass('organizerButton', baseTheme)]}
style={{ backgroundColor: getAccentColor(accentColor, baseTheme) }}
onClick={() => redirectToEventPage()}>
Event Screen
</button>
</div>
</div>

<br />
Expand Down
12 changes: 8 additions & 4 deletions components/Organizer/OrganizerDash.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ import SettingsTab from './SettingsTab/SettingsTab';
import { RequestType, useCustomSWR } from '../../utils/request-utils';
import { UserData } from '../../types/database';
import BugReportsTab from './BugReportsTab/BugReportsTab';
import Link from 'next/link';
import { useRouter } from 'next/router';

export default function OrganizerDash() {
// Get session data
const { data: session, status } = useSession();
const { data: session } = useSession();
const router = useRouter();
const { accentColor, baseTheme, setAccentColor, setBaseTheme } = useContext(ThemeContext);

// User data
Expand Down Expand Up @@ -45,18 +48,19 @@ export default function OrganizerDash() {
<div>
<div className={styles[getThemedClass('organizerHeader', baseTheme)]}>
<h1 className={styles[getThemedClass('organizerTitle', baseTheme)]}>Organizer Dashboard</h1>

<div className={styles[getThemedClass('organizerHeaderEmail', baseTheme)]}>
<div className={styles[getThemedClass('organizerHeaderEmailText', baseTheme)]}>
{session?.user?.email}
</div>
<div>
<Link href="/event">
<button
className={styles[getThemedClass('organizerButton', baseTheme)]}
style={{ backgroundColor: getAccentColor(accentColor, baseTheme) }}
style={{ backgroundColor: '#888888' }}
onClick={() => signOut()}>
Sign out
</button>
</div>
</Link>
</div>
</div>
<Space direction="vertical">
Expand Down
51 changes: 46 additions & 5 deletions components/hacker/Leaderboard.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,58 @@
import { UserData, TeamData } from '../../types/database';
import { RequestType, useCustomSWR } from '../../utils/request-utils';
import styles from '../../styles/hacker/Table.module.css';
import { useEffect, useState } from 'react';
import { set } from 'mongoose';

interface LeaderboardData extends Omit<UserData, 'team'> {
team: TeamData;
}

const Leaderboard = () => {
const Leaderboard = ({ limit = 10, isRotating = false }: { limit?: number; isRotating?: boolean }) => {
const [currentPage, setCurrentPage] = useState<number>(1);
const [totalPages, setTotalPages] = useState<number>(1);
const [filteredData, setFilteredData] = useState<LeaderboardData[] | undefined>(undefined);
const [paginatedLeaderboardData, setPaginatedLeaderboardData] = useState<LeaderboardData[] | undefined>(undefined);

// Leaderboard data
const { data: leaderboardData, error: leaderboardError } = useCustomSWR<LeaderboardData[]>({
url: '/api/leaderboard',
url: `/api/leaderboard?limit=${limit}`,
method: RequestType.GET,
errorMessage: 'Failed to get list of hackers on the leaderboard.',
});

// start a timer that updates the leaderboard every 5 seconds
useEffect(() => {
if (isRotating) {
const interval = setInterval(() => {
if (currentPage >= totalPages) {
setCurrentPage(1);
} else {
setCurrentPage(currentPage + 1);
}
}, 3000);

return () => clearInterval(interval);
}
}, [currentPage, isRotating, totalPages]);

useEffect(() => {
if (filteredData) {
setPaginatedLeaderboardData(undefined);
setPaginatedLeaderboardData(filteredData.slice((currentPage - 1) * 10, currentPage * 10));
setTotalPages(Math.floor(filteredData.length / 10));
}
/* eslint-disable-next-line react-hooks/exhaustive-deps */
}, [currentPage, filteredData]);

useEffect(() => {
if (leaderboardData) {
// filter out the zeros
const filteredData = leaderboardData.filter(entry => entry.nfcPoints > 0);
setFilteredData(filteredData);
}
}, [leaderboardData]);

return (
<div className={styles.Container}>
Points Leaderboard
Expand All @@ -22,21 +61,23 @@ const Leaderboard = () => {
</div>
{leaderboardError ? (
<div className={styles.Placeholder}>Failed to load data.</div>
) : !leaderboardData ? (
) : !filteredData ? (
<div className={styles.Placeholder}>Loading...</div>
) : (
<div className={styles.TableContainer}>
<table>
<thead>
<tr>
<th>Rank</th>
<th>Name</th>
<th>Team</th>
<th>Points</th>
</tr>
</thead>
<tbody>
{leaderboardData?.map(entry => (
<tr key={entry.name}>
{paginatedLeaderboardData?.map((entry, index) => (
<tr key={index}>
<td>{(currentPage - 1) * 10 + index + 1}</td>
<td>{entry.name}</td>
<td>{entry.team?.name}</td>
<td>{entry.nfcPoints}</td>
Expand Down
6 changes: 6 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ module.exports = {
);
}

// add fallback
config.resolve.fallback = {
child_process: false,
fs: false,
};

return config;
},
experimental: { nftTracing: true },
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@types/prompt-sync": "^4.2.2",
"antd": "^5.9.4",
"copy-webpack-plugin": "^11.0.0",
"cron": "^3.1.5",
"csv-writer": "^1.6.0",
"dayjs": "^1.11.10",
"dotenv": "^16.0.2",
Expand Down Expand Up @@ -49,6 +50,7 @@
"@types/faker": "^5.5.8",
"@types/luxon": "^3.0.1",
"@types/mongodb": "^4.0.7",
"@types/node-cron": "^3.0.10",
"@types/react": "17.0.43",
"@types/react-highlight-words": "^0.16.4",
"eslint": "8.24.0",
Expand All @@ -58,5 +60,8 @@
"prettier": "^2.6.2",
"typescript": "4.8.3",
"typescript-plugin-css-modules": "^5.0.1"
},
"browser": {
"child_process": false
}
}
6 changes: 4 additions & 2 deletions pages/api/leaderboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import Team from '../../models/team';

export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const session = await getSession({ req });
if (session?.userType !== 'HACKER') return res.status(403).send('Forbidden');
if (session?.userType !== 'HACKER' && session?.userType !== 'ORGANIZER') return res.status(403).send('Forbidden');

Team; // Don't remove or the import will get optimized out and the populate will fail
await dbConnect();
switch (req.method) {
case 'GET':
const limit = parseInt(req.query.limit as string) || 10;

const users = await User.find({ nfcPoints: { $exists: true } })
.sort({ nfcPoints: -1 })
.limit(10)
.limit(limit)
.populate('team')
.select('name nfcPoints team');

Expand Down
Loading
Loading