Skip to content

Commit

Permalink
feat:ranking-UI (#283)
Browse files Browse the repository at this point in the history
  • Loading branch information
glowisn authored Mar 8, 2024
1 parent 1d5714d commit b29dd94
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 59 deletions.
24 changes: 24 additions & 0 deletions client/src/apis/scoreBoard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { apiClient } from './apiClient';
import { ScoreBoardInformation } from '../types/ScoreBoardInformation';

export async function getScoreBoardInformation(
roomCode: string,
): Promise<ScoreBoardInformation> {
try {
const { data: submissionData } = await apiClient.get('/submission', {
params: {
roomCode,
},
});

const { data: rankingData } = await apiClient.get('/submission/ranking', {
params: {
roomCode,
},
});

return { submissions: submissionData, rankings: rankingData };
} catch (error) {
throw new Error('getScores error');
}
}
15 changes: 0 additions & 15 deletions client/src/apis/scores.ts

This file was deleted.

21 changes: 16 additions & 5 deletions client/src/pages/Room/ScoreBoard/Players.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
import { RxBorderSolid, RxCheck, RxCross1 } from 'react-icons/rx';
import { Score, Status } from '../../../types/Score';
import { Submission, Status } from '../../../types/Submission';

interface PlayersProps {
index: number;
playerName: string;
results: Score[];
results: Submission[];
}

export default function Players({ index, playerName, results }: PlayersProps) {
const getOrdinal = (n: number) => {
if (n === 1) return '1st';
else if (n === 2) return '2nd';
else if (n === 3) return '3rd';
else return n + 'th';
};

return (
<li
className="flex flex-row bg-bg px-5 py-2.5 text-text_default odd:bg-bg_secondary"
key={index}>
<div>{playerName}</div>
<div className="flex w-16 justify-center pr-4 text-base font-bold">
{getOrdinal(index + 1)}
</div>
<div className="flex items-center pr-2">{playerName}</div>
<ol className="flex w-full flex-row gap-1">
{results.map((result,index) => (
<li className="flex flex-1 flex-row items-center justify-around" key={index}>
{results.map((result, index) => (
<li
className="flex flex-1 flex-row items-center justify-around"
key={index}>
{result.status === Status.ACCEPTED ? (
<RxCheck className="text-green" strokeWidth={2} size={20} />
) : result.status === Status.WRONG ? (
Expand Down
49 changes: 33 additions & 16 deletions client/src/pages/Room/ScoreBoard/ScoreBoard.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,53 @@
import { useRoom } from '../../../hooks/useRoom';
import { Score } from '../../../types/Score';
import { ScoreBoardInformation } from '../../../types/ScoreBoardInformation';
import { Submission } from '../../../types/Submission';
import Players from './Players';

interface ScoreBoardProps {
scores: Score[];
scores: ScoreBoardInformation;
}

interface PlayerScore {
playerName: string;
results: Submission[];
}

export default function ScoreBoard({ scores }: ScoreBoardProps) {
const { participantNames, problems } = useRoom().roomInfo;


const playerNames: string[] = participantNames;
const problemIds: number[] = problems.map((problem) => problem.bojProblemId);

const playerScores: Array<{ playerName: string; results: Score[] }> =
playerNames.map((playerName) => {
const results: Score[] = problemIds.map((problemId) => {
const score = scores.find(
(score) =>
score.username === playerName && score.bojProblemId === problemId,
);
return score
? score
: { username: playerName, bojProblemId: problemId, status: 'WAITING' };
});
return { playerName: playerName, results: results };
const playerScores: PlayerScore[] = playerNames.map((playerName) => {
const results: Submission[] = problemIds.map((problemId) => {
const submissions = scores.submissions.find(
(score) =>
score.username === playerName && score.bojProblemId === problemId,
);
return (
submissions || {
username: playerName,
bojProblemId: problemId,
status: 'WAITING',
}
);
});
return { playerName, results };
});

const sortedPlayerScores: PlayerScore[] = scores.rankings
.map((ranking) =>
playerScores.find(
(playerScore) => playerScore.playerName === ranking.username,
),
)
.filter(
(playerScore): playerScore is PlayerScore => playerScore !== undefined,
);

return (
<ul className="my-5 flex w-full flex-col overflow-auto text-sm font-medium text-text_default">
{playerScores.map((playerScore, index) => (
{sortedPlayerScores.map((playerScore, index) => (
<Players
key={index}
index={index}
Expand Down
8 changes: 4 additions & 4 deletions client/src/pages/Room/ScoreBoard/ScoreBoardButton.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { FaChartSimple } from 'react-icons/fa6';
import ScoreBoardModal from './ScoreBoardModal';
import { useRef, useState } from 'react';
import { getScores } from '../../../apis/scores';
import { getScoreBoardInformation } from '../../../apis/scoreBoard';
import { useRoom } from '../../../hooks/useRoom';
import { Score } from '../../../types/Score';
import { ScoreBoardInformation } from '../../../types/ScoreBoardInformation';

export default function ScoreboardButton() {
const [isModalOpen, setIsModalOpen] = useState(false);
const [scores, setScores] = useState<Score[]>([]);
const [scores, setScores] = useState<ScoreBoardInformation>({submissions: [], rankings: []});
const modalOverlayRef = useRef<HTMLDivElement>(null);
const { roomCode, roomInfo } = useRoom();

const openModal = async () => {
try {
const res = await getScores(roomCode);
const res : ScoreBoardInformation = await getScoreBoardInformation(roomCode);
setScores(res);
} catch (e) {
console.log(e);
Expand Down
7 changes: 3 additions & 4 deletions client/src/pages/Room/ScoreBoard/ScoreBoardModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ import ScoreBoard from './ScoreBoard';
import { FaChartSimple, FaXmark } from 'react-icons/fa6';
import { RefObject } from 'react';
import { useTheme } from '../../../hooks/useTheme';
import { Score } from '../../../types/Score';
import { ScoreBoardInformation } from '../../../types/ScoreBoardInformation';

interface ModalProps {
scores: Score[];
scores: ScoreBoardInformation;
modalOverlayRef: RefObject<HTMLDivElement>;
closeModal: () => void;
modalOutsideClick: (arg: React.MouseEvent<HTMLDivElement>) => void;
}


const iconStyle = {
fontSize: '1.5rem',
};
Expand Down Expand Up @@ -44,7 +43,7 @@ export default function ScoreBoardModal({
</div>
<div className="text-xs text-text_default">3 online</div>
</div>
<ScoreBoard scores ={scores}/>
<ScoreBoard scores={scores} />
</div>
</div>
);
Expand Down
3 changes: 1 addition & 2 deletions client/src/types/Ranking.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export interface Ranking {
id: number;
username: string;
numberOfProblemsSolved: number;
mostRecentCorrectSubmissionTime: string | null; // is nullable?
mostRecentCorrectSubmissionTime: string | null;
}
13 changes: 0 additions & 13 deletions client/src/types/Score.ts

This file was deleted.

7 changes: 7 additions & 0 deletions client/src/types/ScoreBoardInformation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Submission } from './Submission';
import { Ranking } from './Ranking';

export interface ScoreBoardInformation {
submissions: Submission[];
rankings: Ranking[];
}
13 changes: 13 additions & 0 deletions client/src/types/Submission.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface Submission {
username: string;
bojProblemId: number;
status: Status;
}

export const Status = {
ACCEPTED: 'ACCEPTED',
WRONG: 'WRONG',
WAITING: 'WAITING',
} as const;

type Status = (typeof Status)[keyof typeof Status];

0 comments on commit b29dd94

Please sign in to comment.