Skip to content

Commit 3f633fe

Browse files
authored
Add tabs to judging page (#509)
* improve the readablity for judging page; create assigned and all teams tab; fix the bug in organizer's scoreboard * fix the bugs in model and database; add form to all teams tag in judging page * fix bug in confirm judging sessions api * fix bug in types/database.ts * fix bugs of changing database and model in script
1 parent 2a9d199 commit 3f633fe

File tree

18 files changed

+708
-373
lines changed

18 files changed

+708
-373
lines changed

components/Organizer/JudgingTab/Scoreboard.tsx

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,6 @@ export default function Scoreboard(props: AllScoresProps) {
3232
setSearchText('');
3333
};
3434

35-
const handleSearch = (
36-
selectedKeys: string[],
37-
confirm: (param?: FilterConfirmProps) => void,
38-
dataIndex: string,
39-
closeDropDown: boolean
40-
) => {
41-
confirm({ closeDropdown: closeDropDown });
42-
setSearchText(selectedKeys[0]);
43-
setSearchedColumn(dataIndex);
44-
};
45-
4635
const handleReset = (clearFilters: () => void) => {
4736
clearFilters();
4837
setSearchText('');
@@ -67,9 +56,11 @@ export default function Scoreboard(props: AllScoresProps) {
6756
value={selectedKeys[0]}
6857
onChange={e => {
6958
setSelectedKeys(e.target.value ? [e.target.value] : []);
70-
handleSearch(selectedKeys as string[], confirm, dataIndex, false);
59+
confirm({ closeDropdown: false });
60+
setSearchText(e.target.value);
61+
setSearchedColumn(dataIndex);
7162
}}
72-
onPressEnter={() => handleSearch(selectedKeys as string[], confirm, dataIndex, true)}
63+
onPressEnter={() => confirm({ closeDropdown: true })}
7364
style={{ marginBottom: 8, display: 'block' }}
7465
/>
7566
<Button

components/judges/schedule.tsx renamed to components/Organizer/ScheduleTab/OrganizerSchedule.tsx

Lines changed: 3 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
import { Space, Table, Collapse, Tag, Switch, Button, notification, Upload, Spin, theme, Radio } from 'antd';
2-
import React, { useContext, useMemo, useState } from 'react';
1+
import { Space, Table, Tag } from 'antd';
2+
import React, { useMemo, useState } from 'react';
33
import { DateTime } from 'luxon';
4-
import Link from 'next/link';
5-
import { JudgingSessionData } from '../../types/database';
6-
import { User } from 'next-auth';
7-
import { ThemeContext, getAccentColor } from '../../theme/themeProvider';
4+
import { JudgingSessionData } from '../../../types/database';
85

96
interface ScheduleProps {
107
data: JudgingSessionData[];
@@ -181,101 +178,3 @@ export default function OrganizerSchedule(props: ScheduleProps) {
181178
</div>
182179
);
183180
}
184-
185-
export function JudgeSchedule({ data, handleChange }: ScheduleProps) {
186-
const [isJudged, setIsJudged] = useState(false);
187-
const { accentColor, baseTheme } = useContext(ThemeContext);
188-
189-
const columns = [
190-
{
191-
title: 'Table',
192-
dataIndex: 'table',
193-
key: 'table',
194-
width: '10%',
195-
render: (locationNum: number) => locationNum,
196-
},
197-
{
198-
title: 'Project',
199-
dataIndex: 'project',
200-
key: 'project',
201-
width: '40%',
202-
render: ({ name, link }: { name: string; link: URL }) => (
203-
<>
204-
<td>{name}</td>
205-
<Link href={link} passHref>
206-
<a
207-
style={{ color: getAccentColor(accentColor, baseTheme), textDecoration: 'underline' }}
208-
target="_blank">
209-
Devpost
210-
</a>
211-
</Link>
212-
</>
213-
),
214-
},
215-
{
216-
title: 'Team Members',
217-
dataIndex: 'teamMembers',
218-
key: 'teamMembers',
219-
width: '40%',
220-
render: (members: User[]) => members.map(member => <Tag key={member.id}>{member.name}</Tag>),
221-
},
222-
{
223-
title: 'Judgement State',
224-
dataIndex: 'haveJudged',
225-
key: 'haveJudged',
226-
width: '10%',
227-
render: (haveJudged: []) => <Tag>{haveJudged ? 'Judged' : 'Without Judgement'}</Tag>,
228-
},
229-
];
230-
231-
const dataSource = data
232-
.filter(item => (isJudged ? item.haveJudged : !item.haveJudged))
233-
.map(item => ({
234-
table: item.team.locationNum,
235-
project: { name: item.team.name, link: new URL(item.team.devpost) },
236-
teamMembers: item.team.members,
237-
teamId: item.team._id,
238-
haveJudged: item.haveJudged,
239-
}));
240-
241-
const handleRowClick = (record: any) => {
242-
handleChange(record.teamId);
243-
};
244-
245-
return (
246-
<Table
247-
locale={{
248-
emptyText: (
249-
<div style={{ paddingTop: '50px', paddingBottom: '50px' }}>
250-
<h3>
251-
{data.length == 0
252-
? 'Stay tuned! You will see your teams that you will judge soon!'
253-
: isJudged
254-
? "You haven't started judging yet."
255-
: "Hurraaaaarrgh! You're off duty!"}
256-
</h3>
257-
</div>
258-
),
259-
}}
260-
dataSource={dataSource}
261-
columns={columns}
262-
pagination={false}
263-
bordered
264-
scroll={{ x: true }}
265-
summary={_ => (
266-
<Table.Summary fixed={true}>
267-
<Table.Summary.Row>
268-
<Table.Summary.Cell index={0} colSpan={6}>
269-
<Button type="primary" onClick={() => setIsJudged(!isJudged)} style={{ marginLeft: 8 }}>
270-
{isJudged ? 'Judged' : 'Without Judgement'}
271-
</Button>
272-
</Table.Summary.Cell>
273-
</Table.Summary.Row>
274-
</Table.Summary>
275-
)}
276-
onRow={record => ({
277-
onClick: () => handleRowClick(record),
278-
})}
279-
/>
280-
);
281-
}

components/Organizer/ScheduleTab/ScheduleTab.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Button, InputNumber, Slider, Row, Col } from 'antd';
22
import { SetStateAction, useContext, useEffect, useState } from 'react';
33
import { matchTeams, handleConfirmSchedule } from '../../../utils/organizer-utils';
4-
import OrganizerSchedule, { generateTimes } from '../../judges/schedule';
4+
import OrganizerSchedule, { generateTimes } from './OrganizerSchedule';
55
import { ResponseError, JudgingSessionData, UserData, TeamData, HackathonSettingsData } from '../../../types/database';
66
import Title from 'antd/lib/typography/Title';
77
import { RequestType, useCustomSWR } from '../../../utils/request-utils';
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import { FilterConfirmProps, FilterValue, SorterResult } from 'antd/es/table/interface';
2+
import { useContext, useRef, useState } from 'react';
3+
import { TeamData } from '../../../types/database';
4+
import { Button, Input, InputRef, Table } from 'antd';
5+
import { getAccentColor, ThemeContext } from '../../../theme/themeProvider';
6+
import { SearchOutlined } from '@ant-design/icons';
7+
import Highlighter from 'react-highlight-words';
8+
import Link from 'next/link';
9+
10+
interface AllTeamsProps {
11+
teamsData: TeamData[];
12+
teamId: string;
13+
handleTeamChange: (teamId: string) => void;
14+
}
15+
16+
export const AllTeamsForm = ({ teamsData, teamId, handleTeamChange }: AllTeamsProps) => {
17+
const [filteredInfo, setFilteredInfo] = useState<Record<string, FilterValue | null>>({});
18+
const [sortedInfo, setSortedInfo] = useState<SorterResult<TeamData>>({});
19+
const [searchedColumn, setSearchedColumn] = useState('');
20+
const [searchText, setSearchText] = useState('');
21+
const searchInput = useRef<InputRef>(null);
22+
const { accentColor, baseTheme } = useContext(ThemeContext);
23+
24+
const handleChange = (pagination: any, filters: any, sorter: any) => {
25+
setSortedInfo(sorter as SorterResult<TeamData>);
26+
setFilteredInfo(filters);
27+
};
28+
29+
const handleReset = (clearFilters: () => void) => {
30+
clearFilters();
31+
setSearchText('');
32+
};
33+
34+
const getColumnSearchProps = (dataIndex: string) => ({
35+
filterDropdown: ({
36+
setSelectedKeys,
37+
selectedKeys,
38+
confirm,
39+
clearFilters,
40+
}: {
41+
setSelectedKeys: (selectedKeys: React.Key[]) => void;
42+
selectedKeys: React.Key[];
43+
confirm: (param?: FilterConfirmProps) => void;
44+
clearFilters: () => void;
45+
}) => (
46+
<div style={{ padding: 8 }}>
47+
<Input
48+
ref={searchInput}
49+
placeholder={`Search ${dataIndex}`}
50+
value={selectedKeys[0]}
51+
onChange={e => {
52+
setSelectedKeys(e.target.value ? [e.target.value] : []);
53+
confirm({ closeDropdown: false });
54+
setSearchText(e.target.value);
55+
setSearchedColumn(dataIndex);
56+
}}
57+
onPressEnter={() => confirm({ closeDropdown: true })}
58+
style={{ marginBottom: 8, display: 'block' }}
59+
/>
60+
<Button
61+
onClick={() => {
62+
clearFilters && handleReset(clearFilters);
63+
confirm({ closeDropdown: false });
64+
}}
65+
style={{ width: '100%' }}>
66+
Reset
67+
</Button>
68+
</div>
69+
),
70+
filterIcon: (filtered: boolean) => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />,
71+
onFilter: (value: string | number | boolean, record: any): boolean => {
72+
const recordValue = dataIndex in record ? record[dataIndex] : record.application?.[dataIndex];
73+
if (recordValue === undefined || recordValue === null) {
74+
return false;
75+
}
76+
return recordValue.toString().toLowerCase().includes(value.toString().toLowerCase());
77+
},
78+
filteredValue:
79+
(dataIndex in filteredInfo ? filteredInfo[dataIndex] : filteredInfo['application.' + dataIndex]) || null,
80+
onFilterDropdownOpenChange: (open: boolean) => {
81+
if (open) {
82+
setTimeout(() => searchInput.current?.select(), 100);
83+
}
84+
},
85+
render: (text: string) =>
86+
searchedColumn === dataIndex ? (
87+
<Highlighter
88+
highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
89+
searchWords={[searchText]}
90+
autoEscape
91+
textToHighlight={text?.toString() ?? ''}
92+
/>
93+
) : (
94+
text
95+
),
96+
});
97+
98+
const columns = [
99+
{
100+
title: 'Table',
101+
dataIndex: 'locationNum',
102+
key: 'locationNum',
103+
width: '20%',
104+
sorter: (a: any, b: any) => a.locationNum - b.locationNum,
105+
sortOrder: sortedInfo.columnKey === 'locationNum' ? sortedInfo.order : null,
106+
},
107+
{
108+
title: 'Team',
109+
dataIndex: 'name',
110+
key: 'name',
111+
width: '40%',
112+
...getColumnSearchProps('name'),
113+
},
114+
{
115+
title: 'Devpost',
116+
dataIndex: 'devpost',
117+
key: 'devpost',
118+
width: '40%',
119+
render: (link: URL) => {
120+
return (
121+
<>
122+
<Link href={link} passHref>
123+
<a
124+
style={{ color: getAccentColor(accentColor, baseTheme), textDecoration: 'underline' }}
125+
target="_blank">
126+
{link}
127+
</a>
128+
</Link>
129+
</>
130+
);
131+
},
132+
},
133+
];
134+
135+
return (
136+
<>
137+
<Table
138+
dataSource={teamsData}
139+
columns={columns}
140+
onChange={handleChange}
141+
sortDirections={['descend', 'ascend']}
142+
onRow={record => ({
143+
onClick: () => handleTeamChange(teamId !== String(record._id) ? String(record._id) : ''),
144+
})}
145+
/>
146+
</>
147+
);
148+
};

0 commit comments

Comments
 (0)