Skip to content

Commit 81fb3e9

Browse files
created mock tanstack table for leaderboard
1 parent 852a30e commit 81fb3e9

8 files changed

Lines changed: 219 additions & 39 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"react-dom": "^18.3.1",
4343
"react-hook-form": "^7.54.2",
4444
"react-hot-toast": "^2.4.1",
45+
"react-icons": "^5.4.0",
4546
"shadcn-ui": "^0.9.4",
4647
"tailwind-merge": "^2.5.2",
4748
"tailwindcss-animate": "^1.0.7",

pnpm-lock.yaml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/api/fetchScores.ts

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,48 @@ import axios from "./axiosConfig";
33
import { z } from "zod";
44

55
const scoreSchema = z.object({
6-
id: z.string(),
7-
team_id: z.string(),
8-
design: z.number(),
9-
implementation: z.number(),
10-
presentation: z.number(),
11-
innovation: z.number(),
12-
teamwork: z.number(),
13-
comment: z.string(),
14-
round: z.number(),
6+
id: z.string(),
7+
team_id: z.string(),
8+
design: z.number(),
9+
implementation: z.number(),
10+
presentation: z.number(),
11+
innovation: z.number(),
12+
teamwork: z.number(),
13+
comment: z.string(),
14+
round: z.number(),
1515
});
1616

1717
const scoresResponseSchema = z.object({
1818
message: z.string(),
19-
data: z.object({
20-
scores: z.array(scoreSchema)
21-
}).optional()
19+
data: z
20+
.object({
21+
scores: z.array(scoreSchema),
22+
})
23+
.optional(),
2224
});
2325

2426
const createUpdateResponseSchema = z.object({
2527
message: z.string(),
26-
data: z.object({
27-
message: z.string()
28-
}).optional()
28+
data: z
29+
.object({
30+
message: z.string(),
31+
})
32+
.optional(),
2933
});
3034

3135
const deleteResponseSchema = z.object({
3236
status: z.string(),
33-
message: z.string()
37+
message: z.string(),
3438
});
3539

3640
type ScoreResponse = z.infer<typeof scoreSchema>;
3741

38-
interface CreateScoreRequest extends Omit<z.infer<typeof scoreSchema>, 'id'> {
42+
interface CreateScoreRequest extends Omit<z.infer<typeof scoreSchema>, "id"> {
3943
team_id: string;
4044
}
4145

42-
interface UpdateScoreRequest extends Partial<Omit<CreateScoreRequest, 'team_id'>> {
46+
interface UpdateScoreRequest
47+
extends Partial<Omit<CreateScoreRequest, "team_id">> {
4348
scoreId: string;
4449
team_id?: string;
4550
}
@@ -59,31 +64,27 @@ export const fetchScores = async (teamId: string): Promise<ScoreResponse[]> => {
5964
const parsedResponse = scoresResponseSchema.parse(response.data);
6065
return parsedResponse.data?.scores ?? [];
6166
} catch (err) {
62-
const error=err as AxiosError
67+
const error = err as AxiosError;
6368
if (error.status === 404) {
6469
return [];
6570
}
66-
throw new Error(error.message || 'Failed to fetch scores');
71+
throw new Error(error.message || "Failed to fetch scores");
6772
}
6873
};
6974

7075
export const createScore = async (data: CreateScoreRequest) => {
7176
try {
72-
const response = await axios.post(
73-
'panel/createscore',
74-
data,
75-
{
76-
withCredentials: true,
77-
}
78-
);
77+
const response = await axios.post("panel/createscore", data, {
78+
withCredentials: true,
79+
});
7980
const parsedResponse = createUpdateResponseSchema.parse(response.data);
8081
return parsedResponse;
8182
} catch (err) {
82-
const error=err as AxiosError
83+
const error = err as AxiosError;
8384
if (error.message) {
8485
throw new Error(error.message);
8586
}
86-
throw new Error('Failed to create score');
87+
throw new Error("Failed to create score");
8788
}
8889
};
8990

@@ -92,12 +93,12 @@ export const deleteScore = async (scoreId: string) => {
9293
const response = await axios.delete(`panel/deletescore/${scoreId}`, {
9394
withCredentials: true,
9495
});
95-
96+
9697
const parsedResponse = deleteResponseSchema.parse(response.data);
9798
return parsedResponse;
9899
} catch (err) {
99-
const error=err as AxiosError
100-
throw new Error(error.message || 'Failed to delete score');
100+
const error = err as AxiosError;
101+
throw new Error(error.message || "Failed to delete score");
101102
}
102103
};
103104

@@ -108,15 +109,15 @@ export const updateScore = async (data: UpdateScoreRequest) => {
108109
data,
109110
{
110111
withCredentials: true,
111-
}
112+
},
112113
);
113114
const parsedResponse = createUpdateResponseSchema.parse(response.data);
114115
return parsedResponse;
115116
} catch (err) {
116-
const error=err as AxiosError
117+
const error = err as AxiosError;
117118
if (error.message) {
118119
throw new Error(error.message);
119120
}
120-
throw new Error('Failed to update score');
121+
throw new Error("Failed to update score");
121122
}
122-
};
123+
};

src/api/leaderboard.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { type UserResponse } from "@/data/schema";
2+
import { usersResponseSchema } from "@/data/schema";
3+
import axios from "./axiosConfig";
4+
import { z } from "zod";
5+
export const leaderboardSchema = z.object({
6+
ID: z.string(),
7+
team_id: z.string(),
8+
team_name: z.string(),
9+
design: z.number(),
10+
implementation: z.number(),
11+
presentation: z.number(),
12+
innovation: z.number(),
13+
teamwork: z.number(),
14+
comment: z.string(),
15+
total_score: z.number(),
16+
});
17+
18+
export type Leaderboard = z.infer<typeof leaderboardSchema>;
19+
20+
export const leaderBoardResponseSchema = z.object({
21+
status: z.string(),
22+
message: z.string(),
23+
data: z.object({
24+
message: z.string().optional(),
25+
users: z.array(leaderboardSchema).nullable(),
26+
}),
27+
});
28+
export type LeaderboardResponse = z.infer<typeof leaderBoardResponseSchema>;
29+
30+
export const fetchLeaderboard = async ({
31+
limit,
32+
cursorId,
33+
name,
34+
}: {
35+
limit: number;
36+
cursorId?: string;
37+
name?: string;
38+
}) => {
39+
try {
40+
const params = new URLSearchParams({ limit: String(limit) });
41+
42+
if (name) {
43+
params.append("name", name);
44+
} else if (cursorId) {
45+
params.append("cursor", cursorId);
46+
}
47+
48+
const url = `admin/users?${params.toString()}`;
49+
50+
const response = await axios.get<LeaderboardResponse>(url);
51+
52+
const parsedResponse = leaderBoardResponseSchema.parse(response.data);
53+
const users = parsedResponse.data.users;
54+
console.log(users);
55+
const nextCursor = users != null ? users[users.length - 1]?.ID : null;
56+
57+
return {
58+
users,
59+
nextCursor,
60+
};
61+
} catch (err) {
62+
console.log(err);
63+
throw err;
64+
}
65+
};

src/app/leaderboard/page.tsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"use client";
2+
import {columns} from "@/components/columns/LeaderBoardCol";
3+
import { DataTable } from "@/components/table/data-table";
4+
import { fetchLeaderboard, Leaderboard, LeaderboardResponse } from "@/api/leaderboard";
5+
import loading from "@/assets/images/loading.gif";
6+
import { Button } from "@/components/ui/button";
7+
import { type User } from "@/data/schema";
8+
import { useQuery } from "@tanstack/react-query";
9+
import Image from "next/image";
10+
import { useState } from "react";
11+
import { useDebounce } from "use-debounce";
12+
13+
export default function LeaderBoard() {
14+
const [cursorHistory, setCursorHistory] = useState<string[]>([]);
15+
const [currentCursor, setCurrentCursor] = useState<string | undefined>(
16+
undefined,
17+
);
18+
const [pageLimit, setPageLimit] = useState<number>(10);
19+
const [theName, setTheName] = useState<string>("");
20+
// const queryClient = useQueryClient();
21+
const [nameDebounce] = useDebounce(theName, 1000);
22+
const {
23+
data: userList,
24+
isLoading,
25+
isError,
26+
} = useQuery({
27+
queryKey: ["leaderboard", currentCursor, nameDebounce, pageLimit],
28+
queryFn: () =>
29+
fetchLeaderboard({
30+
limit: pageLimit,
31+
cursorId: currentCursor,
32+
name: nameDebounce,
33+
}),
34+
placeholderData: (previousData) => previousData,
35+
// enabled: !!debouncedSearch,
36+
});
37+
38+
const handleNextPage = () => {
39+
if (userList?.nextCursor) {
40+
console.log("yes cursor available");
41+
setCursorHistory((prev) => [...prev, currentCursor ?? ""]); // Store current cursor
42+
setCurrentCursor(userList.nextCursor); // Move to the next page
43+
}
44+
};
45+
46+
const handlePrevPage = () => {
47+
if (cursorHistory.length > 0) {
48+
const prevCursor = cursorHistory[cursorHistory.length - 1]; // Get last cursor
49+
setCursorHistory((prev) => prev.slice(0, -1)); // Remove last cursor from history
50+
setCurrentCursor(prevCursor ?? undefined); // Move to previous page
51+
}
52+
};
53+
54+
return (
55+
<div className="p-4">
56+
{isError && <div className="text-red-500">Error fetching team data</div>}
57+
58+
{isLoading && (
59+
<div className="flex justify-center">
60+
<Image
61+
className="w-[50%]"
62+
src={loading}
63+
width={100}
64+
height={100}
65+
alt="Loading..."
66+
/>
67+
</div>
68+
)}
69+
70+
<div className="w-full overflow-hidden">
71+
<DataTable<Leaderboard, string>
72+
setPageLimit={setPageLimit}
73+
pageLimit={pageLimit}
74+
columns={columns}
75+
data={userList?.users ?? []}
76+
// data={oosers}
77+
handleNextPage={handleNextPage}
78+
handlePrevPage={handlePrevPage}
79+
/>
80+
</div>
81+
</div>
82+
);
83+
}

src/app/teams/page.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
"use client";
22
import { DataTable } from "@/components/table/data-table";
3-
import columns from "@/components/columns";
43
import teamCol from "@/components/columns/TeamCol";
54
// import { useEffect, useMemo, useState } from "react";
65
// import { user } from "@/store/interfaces";
7-
import tooms from "@/components/dumTeams.json";
86
// import useToast from "@/lib/toast";
97
import { useQuery, useQueryClient } from "@tanstack/react-query";
108
import { type Team } from "@/data/schema";
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"use client";
2+
import { leaderboardSchema } from "@/api/leaderboard";
3+
import { ColumnDef } from "@tanstack/react-table";
4+
import { z } from "zod";
5+
6+
export const columns: ColumnDef<z.infer<typeof leaderboardSchema>>[] = [
7+
{
8+
accessorKey: "team_id",
9+
header: "Year",
10+
},
11+
{
12+
accessorKey: "team_name",
13+
header: "Team Name",
14+
},
15+
{
16+
accessorKey: "scores",
17+
header: "Scores",
18+
cell: ({ row }) => <ViewScores row={row} />,
19+
},
20+
];

src/data/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export const usersResponseSchema = z.object({
4444

4545

4646

47-
47+
4848
export const teamSchema = z.object({
4949
ID: z.string().nullable(),
5050
Name: z.string().nullable(),

0 commit comments

Comments
 (0)