Skip to content

Commit e1beb70

Browse files
authored
Merge pull request #11 from x-team/feature/XTG-320-part2
[XTG-320] - Creating Leaderboard Results and Achievement Rank pages
2 parents 74b7abd + d740a25 commit e1beb70

15 files changed

+353
-247
lines changed

src/App.tsx

+6-6
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ import TowerGamePage from "./pages/TowerGamePage";
1515
import FloorsEditorPage from "./pages/FloorsEditorPage";
1616
import SignInPage from "./pages/SignInPage";
1717
import HomePage from "./pages/HomePage";
18+
import AchievementResultsPage from "./pages/AchievementResultsPage";
19+
import LeaderboardRanksPage from "./pages/LeaderboardRanksPage";
1820

1921
// import useCurrentUser from "./hooks/useCurrentUser";
2022
// import { SyncLoader } from "react-spinners";
2123
// import { XTEAM_ACCENT_COLOR } from "./helpers/colors";
2224
// import { UnauthorizedPage } from "./pages/UnauthorizedPage";
2325
import { ProtectedRoute } from "./ProtectedRoute";
2426
import { AppMenu } from "./AppMenu";
25-
import AchievementsPage from "./pages/AchievementsPage";
26-
import LeaderboardsPage from "./pages/LeaderboardsPage";
2727

2828
export const App = () => {
2929
// if (isDoingInitialLoading) {
@@ -69,21 +69,21 @@ export const App = () => {
6969
}
7070
/>
7171
<Route
72-
path="/games/achievements/:achievementId"
72+
path="/games/:gameTypeId/achievements/:achievementId"
7373
element={
7474
<ProtectedRoute>
7575
<AppMenu>
76-
<AchievementsPage />
76+
<AchievementResultsPage />
7777
</AppMenu>
7878
</ProtectedRoute>
7979
}
8080
/>
8181
<Route
82-
path="/games/leaderboards/:leaderboardId"
82+
path="/games/:gameTypeId/leaderboards/:leaderboardId"
8383
element={
8484
<ProtectedRoute>
8585
<AppMenu>
86-
<LeaderboardsPage />
86+
<LeaderboardRanksPage />
8787
</AppMenu>
8888
</ProtectedRoute>
8989
}

src/api/achievements.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,22 @@ export const getAchievements = async (gameTypeId: number) => {
1010
return achievements;
1111
};
1212

13+
export const getAchievementsProgress = async (gameTypeId: number , achievementId: number) => {
14+
const axios = await getAxiosInstance();
15+
16+
const endpoint = gamesHqUrl + `/dashboard/game-dev/games/${gameTypeId}/achievements/${achievementId}/progress`;
17+
const response = await axios.get(endpoint);
18+
const achievements = response.data as IAchievementUnlocked[];
19+
20+
return achievements;
21+
};
22+
1323
export const getAchievement = async (gameTypeId: number, achievementId: number) => {
1424
const axios = await getAxiosInstance();
1525

1626
const endpoint = gamesHqUrl + `/dashboard/game-dev/games/${gameTypeId}/achievements/${achievementId}`;
1727
const response = await axios.get(endpoint);
18-
const acheivement = response.data.game as IGameType;
28+
const acheivement = response.data as IAchievementUnlocked[];
1929

2030
return acheivement;
2131
};

src/api/leaderboards.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,17 @@ export const getLeaderboard = async (gameTypeId: number, leaderboardId: number)
1515

1616
const endpoint = gamesHqUrl + `/dashboard/game-dev/games/${gameTypeId}/leaderboards/${leaderboardId}`;
1717
const response = await axios.get(endpoint);
18-
const game = response.data.game as IGameType;
18+
const game = response.data.game as ILeaderboard;
19+
20+
return game;
21+
};
22+
23+
export const getLeaderboardResults = async (gameTypeId: number, leaderboardId: number) => {
24+
const axios = await getAxiosInstance();
25+
26+
const endpoint = gamesHqUrl + `/dashboard/game-dev/games/${gameTypeId}/leaderboards/${leaderboardId}/results`;
27+
const response = await axios.get(endpoint);
28+
const game = response.data as ILeaderboardResult[];
1929

2030
return game;
2131
};

src/pages/AchievementResultsPage.tsx

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { useEffect, useState } from "react";
2+
import { useNavigate, useParams } from "react-router-dom";
3+
import { getAchievementsProgress } from "../api/achievements";
4+
import Button from "../ui/Button";
5+
6+
7+
const AchievementResultsPage = () => {
8+
const [achievementResults, setAchievementResults] = useState<IAchievementUnlocked[] | undefined>();
9+
const navigate = useNavigate();
10+
const { gameTypeId, achievementId } = useParams<{
11+
gameTypeId: string;
12+
achievementId: string;
13+
}>();
14+
15+
useEffect(() => {
16+
async function fetchAchievementProgress() {
17+
if (!gameTypeId || !achievementId) {
18+
return;
19+
}
20+
21+
const achievementsRes = await getAchievementsProgress(
22+
Number(gameTypeId),
23+
Number(achievementId)
24+
);
25+
26+
setAchievementResults(achievementsRes);
27+
return achievementsRes;
28+
}
29+
30+
fetchAchievementProgress();
31+
}, [gameTypeId, achievementId]);
32+
33+
return (
34+
<div>
35+
<h2 className="text-2xl font-bold italic font-sans mb-8">ACHIEVEMENT RANK</h2>
36+
37+
<table className="shadow-lg bg-white border-collapse w-full mb-8">
38+
<thead>
39+
<tr>
40+
<th className="bg-gray-100 border text-left px-8 py-4">User</th>
41+
<th className="bg-gray-100 border text-left px-8 py-4">
42+
Rank
43+
</th>
44+
</tr>
45+
</thead>
46+
<tbody>
47+
{achievementResults &&
48+
achievementResults?.map((achievementRank: IAchievementUnlocked, index) => (
49+
// Despite it's not a good practice using index as a key, it's the only way
50+
// to certify key is unique for every row, since AchievementUnlocked table won't have an id.
51+
<tr key={index}>
52+
<td className="border px-8 py-4">{achievementRank._user?.email}</td>
53+
<td className="border px-8 py-4">
54+
{achievementRank?.progress}
55+
</td>
56+
</tr>
57+
))}
58+
</tbody>
59+
</table>
60+
<Button onClick={() => navigate(`/games/${gameTypeId}`)}>Back</Button>
61+
</div>
62+
);
63+
};
64+
65+
export default AchievementResultsPage;

src/pages/AchievementsPage.tsx

-33
This file was deleted.

src/pages/GameEditorPage.tsx

+87-65
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { FormikHelpers, useFormik } from "formik";
22
import { useEffect, useState } from "react";
3-
import { Link } from "react-router-dom";
43
import { useParams, useNavigate } from "react-router-dom";
54
import { SyncLoader } from "react-spinners";
65
import * as Yup from "yup";
@@ -237,40 +236,48 @@ const GameEditorPage = function GameEditorPage({ editMode }: IProps) {
237236
</Button>
238237
</div>
239238
<table className="shadow-lg bg-white border-collapse w-full">
240-
<tr>
241-
<th className="bg-gray-100 border text-left px-8 py-4">id</th>
242-
<th className="bg-gray-100 border text-left px-8 py-4">name</th>
243-
<th className="bg-gray-100 border text-left px-8 py-4">
244-
scoreStrategy
245-
</th>
246-
<th className="bg-gray-100 border text-left px-8 py-4">
247-
resetStrategy
248-
</th>
249-
<th className="bg-gray-100 border text-left px-8 py-4">Edit</th>
250-
</tr>
251-
{currentGameType?._leaderboards?.map(
252-
(leaderboard: ILeaderboard) => (
253-
<tr key={`leaderboard${leaderboard.id}`}>
254-
<td className="border px-8 py-4" ><Link to={`/games/leaderboards/${leaderboard.id}`}>{leaderboard.id}</Link></td>
255-
<td className="border px-8 py-4"><Link to={`/games/leaderboards/${leaderboard.id}`}>{leaderboard.name}</Link></td>
256-
<td className="border px-8 py-4">
257-
<Link to={`/games/leaderboards/${leaderboard.id}`}>{leaderboard.scoreStrategy}</Link>
258-
</td>
259-
<td className="border px-8 py-4">
260-
<Link to={`/games/leaderboards/${leaderboard.id}`}>{leaderboard.resetStrategy}</Link>
261-
</td>
262-
<td className="border px-8 py-4">
263-
<Button
264-
onClick={() => {
265-
openLeaderboardModal(leaderboard)
266-
}}
267-
>
268-
Edit
269-
</Button>
270-
</td>
271-
</tr>
272-
)
273-
)}
239+
<thead>
240+
<tr>
241+
<th className="bg-gray-100 border text-left px-8 py-4">id</th>
242+
<th className="bg-gray-100 border text-left px-8 py-4">name</th>
243+
<th className="bg-gray-100 border text-left px-8 py-4">
244+
scoreStrategy
245+
</th>
246+
<th className="bg-gray-100 border text-left px-8 py-4">
247+
resetStrategy
248+
</th>
249+
<th className="bg-gray-100 border text-left px-8 py-4">Edit</th>
250+
</tr>
251+
</thead>
252+
<tbody>
253+
{currentGameType && currentGameType?._leaderboards?.map(
254+
(leaderboard: ILeaderboard) => (
255+
<tr key={leaderboard.id} className="hover:bg-coolGray-50 cursor-pointer" >
256+
<td className="border px-8 py-4" onClick={() => navigate(`/games/${gameTypeId}/leaderboards/${leaderboard.id}`)}>
257+
{leaderboard.id}
258+
</td>
259+
<td className="border px-8 py-4" onClick={() => navigate(`/games/${gameTypeId}/leaderboards/${leaderboard.id}`)}>
260+
{leaderboard.name}
261+
</td>
262+
<td className="border px-8 py-4 capitalize" onClick={() => navigate(`/games/${gameTypeId}/leaderboards/${leaderboard.id}`)}>
263+
{leaderboard.scoreStrategy}
264+
</td>
265+
<td className="border px-8 py-4 capitalize" onClick={() => navigate(`/games/${gameTypeId}/leaderboards/${leaderboard.id}`)}>
266+
{leaderboard.resetStrategy}
267+
</td>
268+
<td className="border px-8 py-4">
269+
<Button
270+
onClick={() => {
271+
openLeaderboardModal(leaderboard)
272+
}}
273+
>
274+
Edit
275+
</Button>
276+
</td>
277+
</tr>
278+
)
279+
)}
280+
</tbody>
274281
</table>
275282
</div>
276283

@@ -284,33 +291,48 @@ const GameEditorPage = function GameEditorPage({ editMode }: IProps) {
284291
</Button>
285292
</div>
286293
<table className="shadow-lg bg-white border-collapse max-w-xs">
287-
<tr>
288-
<th className="bg-gray-100 border text-left px-8 py-4">id</th>
289-
<th className="bg-gray-100 border text-left px-8 py-4">
290-
Description
291-
</th>
292-
<th className="bg-gray-100 border text-left px-8 py-4">
293-
isEnabled
294-
</th>
295-
<th className="bg-gray-100 border text-left px-8 py-4">
296-
targetValue
297-
</th>
298-
<th className="bg-gray-100 border text-left px-8 py-4">
299-
createdAt
300-
</th>
301-
<th className="bg-gray-100 border text-left px-8 py-4">
302-
updatedAt
303-
</th>
304-
<th className="bg-gray-100 border text-left px-8 py-4">Edit</th>
305-
</tr>
306-
{achievements?.map((achievement =>
307-
<tr key={`achievement${achievement.id}`}>
308-
<td className="border px-8 py-4">{achievement.id}</td>
309-
<td className="border px-8 py-4">{achievement.description || "-"}</td>
310-
<td className="border px-8 py-4">{achievement.isEnabled ? "✅" : "❌"}</td>
311-
<td className="border px-8 py-4">{achievement.targetValue || "-"}</td>
312-
<td className="border px-8 py-4">{achievement.createdAt || "-"}</td>
313-
<td className="border px-8 py-4">{achievement.updatedAt || "-"}</td>
294+
<thead>
295+
<tr>
296+
<th className="bg-gray-100 border text-left px-8 py-4">id</th>
297+
<th className="bg-gray-100 border text-left px-8 py-4">
298+
Description
299+
</th>
300+
<th className="bg-gray-100 border text-left px-8 py-4">
301+
isEnabled
302+
</th>
303+
<th className="bg-gray-100 border text-left px-8 py-4">
304+
targetValue
305+
</th>
306+
<th className="bg-gray-100 border text-left px-8 py-4">
307+
createdAt
308+
</th>
309+
<th className="bg-gray-100 border text-left px-8 py-4">
310+
updatedAt
311+
</th>
312+
<th className="bg-gray-100 border text-left px-8 py-4">Edit</th>
313+
</tr>
314+
</thead>
315+
<tbody>
316+
{achievements && achievements?.map((achievement =>
317+
<tr key={achievement.id} className="hover:bg-coolGray-50 cursor-pointer">
318+
<td className="border px-8 py-4" onClick={() => navigate(`/games/${gameTypeId}/achievements/${achievement.id}`)}>
319+
{achievement.id}
320+
</td>
321+
<td className="border px-8 py-4" onClick={() => navigate(`/games/${gameTypeId}/achievements/${achievement.id}`)}>
322+
{achievement.description || "-"}
323+
</td>
324+
<td className="border px-8 py-4 text-center" onClick={() => navigate(`/games/${gameTypeId}/achievements/${achievement.id}`)}>
325+
{achievement.isEnabled ? "✅" : "❌"}
326+
</td>
327+
<td className="border px-8 py-4" onClick={() => navigate(`/games/${gameTypeId}/achievements/${achievement.id}`)}>
328+
{achievement.targetValue || "-"}
329+
</td>
330+
<td className="border px-8 py-4" onClick={() => navigate(`/games/${gameTypeId}/achievements/${achievement.id}`)}>
331+
{new Date(achievement?.createdAt || "").toLocaleString() }
332+
</td>
333+
<td className="border px-8 py-4" onClick={() => navigate(`/games/${gameTypeId}/achievements/${achievement.id}`)}>
334+
{new Date(achievement?.updatedAt || "").toLocaleString() }
335+
</td>
314336
<td className="border px-8 py-4">
315337
<Button
316338
onClick={() => {
@@ -320,14 +342,14 @@ const GameEditorPage = function GameEditorPage({ editMode }: IProps) {
320342
Edit
321343
</Button>
322344
</td>
323-
324345
</tr>
325346
))}
347+
</tbody>
326348
</table>
327349
</div>
328350
</div>
329-
<AddOrEditAchievementModal show={showModal} onClose={handlePostSubmitAchievement} selectedAchievement={selectedAchievement}/>
330-
<AddOrEditLeaderboardModal show={showLeaderboardModal} onClose={handlePostSubmitLeaderboard} selectedLeaderboard={selectedLeaderboard}/>
351+
{showModal && <AddOrEditAchievementModal show={showModal} onClose={handlePostSubmitAchievement} selectedAchievement={selectedAchievement}/>}
352+
{showLeaderboardModal && <AddOrEditLeaderboardModal show={showLeaderboardModal} onClose={handlePostSubmitLeaderboard} selectedLeaderboard={selectedLeaderboard}/>}
331353
</>
332354
);
333355
};

0 commit comments

Comments
 (0)