Skip to content

Commit 9be5adb

Browse files
committed
feat: implement ranking table
1 parent f5333e4 commit 9be5adb

File tree

8 files changed

+145
-10
lines changed

8 files changed

+145
-10
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"use client";
2+
3+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
4+
import { graphql } from "@/gql";
5+
import { useSuspenseQuery } from "@apollo/client/react";
6+
7+
const MY_SOLVED_QUESTIONS_COUNT = graphql(`
8+
query MySolvedQuestionsCount {
9+
me {
10+
id
11+
name
12+
submissionStatistics {
13+
solvedQuestions
14+
}
15+
}
16+
}
17+
`);
18+
19+
export default function SolvedQuestionsRanking() {
20+
const { data } = useSuspenseQuery(MY_SOLVED_QUESTIONS_COUNT);
21+
22+
return (
23+
<Table>
24+
<TableHeader>
25+
<TableRow>
26+
<TableHead>姓名</TableHead>
27+
<TableHead>解題數</TableHead>
28+
</TableRow>
29+
</TableHeader>
30+
31+
<TableBody>
32+
<TableRow>
33+
<TableCell>{data.me.name}</TableCell>
34+
<TableCell>{data.me.submissionStatistics.solvedQuestions}</TableCell>
35+
</TableRow>
36+
</TableBody>
37+
</Table>
38+
);
39+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { Skeleton } from "@/components/ui/skeleton";
2+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
3+
import { Suspense } from "react";
4+
import SolvedQuestionsRanking from "./completed-questions";
5+
import PointsRanking from "./points";
6+
7+
export default function Ranking() {
8+
return (
9+
<Tabs defaultValue="daily-points">
10+
<TabsList>
11+
<TabsTrigger value="daily-points">每日 - 點數</TabsTrigger>
12+
<TabsTrigger value="weekly-points">每週 - 點數</TabsTrigger>
13+
<TabsTrigger value="daily-solved-questions">每日 - 做題數</TabsTrigger>
14+
<TabsTrigger value="weekly-solved-questions">每週 - 做題數</TabsTrigger>
15+
</TabsList>
16+
17+
<TabsContent value="daily-points">
18+
<Suspense fallback={<Skeleton className="h-48" />}>
19+
<PointsRanking />
20+
</Suspense>
21+
</TabsContent>
22+
<TabsContent value="weekly-points">
23+
<Suspense fallback={<Skeleton className="h-48" />}>
24+
<PointsRanking />
25+
</Suspense>
26+
</TabsContent>
27+
<TabsContent value="daily-solved-questions">
28+
<Suspense fallback={<Skeleton className="h-48" />}>
29+
<SolvedQuestionsRanking />
30+
</Suspense>
31+
</TabsContent>
32+
<TabsContent value="weekly-solved-questions">
33+
<Suspense fallback={<Skeleton className="h-48" />}>
34+
<SolvedQuestionsRanking />
35+
</Suspense>
36+
</TabsContent>
37+
</Tabs>
38+
);
39+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"use client";
2+
3+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
4+
import { graphql } from "@/gql";
5+
import { useSuspenseQuery } from "@apollo/client/react";
6+
7+
const MY_POINTS = graphql(`
8+
query MyPoints {
9+
me {
10+
id
11+
name
12+
totalPoints
13+
}
14+
}
15+
`);
16+
17+
export default function PointsRanking() {
18+
const { data } = useSuspenseQuery(MY_POINTS);
19+
20+
return (
21+
<Table>
22+
<TableHeader>
23+
<TableRow>
24+
<TableHead>姓名</TableHead>
25+
<TableHead>點數</TableHead>
26+
</TableRow>
27+
</TableHeader>
28+
29+
<TableBody>
30+
<TableRow>
31+
<TableCell>{data.me.name}</TableCell>
32+
<TableCell>{data.me.totalPoints}</TableCell>
33+
</TableRow>
34+
</TableBody>
35+
</Table>
36+
);
37+
}

app/(app)/statistics/page.tsx

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
2-
import { Pickaxe } from "lucide-react";
31
import type { Metadata } from "next";
42
import { Suspense } from "react";
53
import Board from "./_components/board";
4+
import Ranking from "./_components/ranking";
65
import Points from "./_components/statistics/points";
76
import ResolvedQuestions from "./_components/statistics/resolved-questions";
87

@@ -41,14 +40,7 @@ export default function StatisticsPage() {
4140

4241
<section>
4342
<h2 className="mb-2 text-lg font-bold">排行榜</h2>
44-
45-
<Alert>
46-
<Pickaxe />
47-
<AlertTitle>正在實作</AlertTitle>
48-
<AlertDescription>
49-
功能正在實作,這裡先佔位!
50-
</AlertDescription>
51-
</Alert>
43+
<Ranking />
5244
</section>
5345
</div>
5446
</div>

gql/gql.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ type Documents = {
2727
"\n query ChallengeStatistics {\n me {\n id\n submissionStatistics {\n totalQuestions\n solvedQuestions\n attemptedQuestions\n }\n }\n }\n": typeof types.ChallengeStatisticsDocument,
2828
"\n query ListQuestions($where: QuestionWhereInput, $after: Cursor) {\n questions(where: $where, first: 10, after: $after) {\n edges {\n node {\n id\n ...QuestionCard\n ...QuestionSolvedStatus\n }\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n": typeof types.ListQuestionsDocument,
2929
"\n query CompletedQuestions {\n me {\n id\n submissionStatistics {\n totalQuestions\n solvedQuestions\n }\n }\n }\n": typeof types.CompletedQuestionsDocument,
30+
"\n query MySolvedQuestionsCount {\n me {\n id\n name\n submissionStatistics {\n solvedQuestions\n }\n }\n }\n": typeof types.MySolvedQuestionsCountDocument,
31+
"\n query MyPoints {\n me {\n id\n name\n totalPoints\n }\n }\n": typeof types.MyPointsDocument,
3032
"\n query Points {\n me {\n id\n totalPoints\n\n points(first: 5) {\n edges {\n node {\n id\n ...PointFragment\n }\n }\n }\n }\n }\n": typeof types.PointsDocument,
3133
"\n fragment PointFragment on Point {\n description\n points\n }\n": typeof types.PointFragmentFragmentDoc,
3234
"\n query ResolvedQuestions {\n me {\n id\n submissionStatistics {\n totalQuestions\n solvedQuestions\n }\n }\n }\n": typeof types.ResolvedQuestionsDocument,
@@ -51,6 +53,8 @@ const documents: Documents = {
5153
"\n query ChallengeStatistics {\n me {\n id\n submissionStatistics {\n totalQuestions\n solvedQuestions\n attemptedQuestions\n }\n }\n }\n": types.ChallengeStatisticsDocument,
5254
"\n query ListQuestions($where: QuestionWhereInput, $after: Cursor) {\n questions(where: $where, first: 10, after: $after) {\n edges {\n node {\n id\n ...QuestionCard\n ...QuestionSolvedStatus\n }\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n }\n": types.ListQuestionsDocument,
5355
"\n query CompletedQuestions {\n me {\n id\n submissionStatistics {\n totalQuestions\n solvedQuestions\n }\n }\n }\n": types.CompletedQuestionsDocument,
56+
"\n query MySolvedQuestionsCount {\n me {\n id\n name\n submissionStatistics {\n solvedQuestions\n }\n }\n }\n": types.MySolvedQuestionsCountDocument,
57+
"\n query MyPoints {\n me {\n id\n name\n totalPoints\n }\n }\n": types.MyPointsDocument,
5458
"\n query Points {\n me {\n id\n totalPoints\n\n points(first: 5) {\n edges {\n node {\n id\n ...PointFragment\n }\n }\n }\n }\n }\n": types.PointsDocument,
5559
"\n fragment PointFragment on Point {\n description\n points\n }\n": types.PointFragmentFragmentDoc,
5660
"\n query ResolvedQuestions {\n me {\n id\n submissionStatistics {\n totalQuestions\n solvedQuestions\n }\n }\n }\n": types.ResolvedQuestionsDocument,
@@ -128,6 +132,14 @@ export function graphql(source: "\n query ListQuestions($where: QuestionWhereIn
128132
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
129133
*/
130134
export function graphql(source: "\n query CompletedQuestions {\n me {\n id\n submissionStatistics {\n totalQuestions\n solvedQuestions\n }\n }\n }\n"): (typeof documents)["\n query CompletedQuestions {\n me {\n id\n submissionStatistics {\n totalQuestions\n solvedQuestions\n }\n }\n }\n"];
135+
/**
136+
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
137+
*/
138+
export function graphql(source: "\n query MySolvedQuestionsCount {\n me {\n id\n name\n submissionStatistics {\n solvedQuestions\n }\n }\n }\n"): (typeof documents)["\n query MySolvedQuestionsCount {\n me {\n id\n name\n submissionStatistics {\n solvedQuestions\n }\n }\n }\n"];
139+
/**
140+
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
141+
*/
142+
export function graphql(source: "\n query MyPoints {\n me {\n id\n name\n totalPoints\n }\n }\n"): (typeof documents)["\n query MyPoints {\n me {\n id\n name\n totalPoints\n }\n }\n"];
131143
/**
132144
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
133145
*/

gql/graphql.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,6 +1559,16 @@ export type CompletedQuestionsQueryVariables = Exact<{ [key: string]: never; }>;
15591559

15601560
export type CompletedQuestionsQuery = { __typename?: 'Query', me: { __typename?: 'User', id: string, submissionStatistics: { __typename?: 'SubmissionStatistics', totalQuestions: number, solvedQuestions: number } } };
15611561

1562+
export type MySolvedQuestionsCountQueryVariables = Exact<{ [key: string]: never; }>;
1563+
1564+
1565+
export type MySolvedQuestionsCountQuery = { __typename?: 'Query', me: { __typename?: 'User', id: string, name: string, submissionStatistics: { __typename?: 'SubmissionStatistics', solvedQuestions: number } } };
1566+
1567+
export type MyPointsQueryVariables = Exact<{ [key: string]: never; }>;
1568+
1569+
1570+
export type MyPointsQuery = { __typename?: 'Query', me: { __typename?: 'User', id: string, name: string, totalPoints: number } };
1571+
15621572
export type PointsQueryVariables = Exact<{ [key: string]: never; }>;
15631573

15641574

@@ -1623,6 +1633,8 @@ export const SubmissionHistoryDocument = {"kind":"Document","definitions":[{"kin
16231633
export const ChallengeStatisticsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ChallengeStatistics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"me"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"submissionStatistics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalQuestions"}},{"kind":"Field","name":{"kind":"Name","value":"solvedQuestions"}},{"kind":"Field","name":{"kind":"Name","value":"attemptedQuestions"}}]}}]}}]}}]} as unknown as DocumentNode<ChallengeStatisticsQuery, ChallengeStatisticsQueryVariables>;
16241634
export const ListQuestionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ListQuestions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"QuestionWhereInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"after"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Cursor"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"questions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"10"}},{"kind":"Argument","name":{"kind":"Name","value":"after"},"value":{"kind":"Variable","name":{"kind":"Name","value":"after"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"QuestionCard"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"QuestionSolvedStatus"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"QuestionSolvedStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Question"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"solved"}},{"kind":"Field","name":{"kind":"Name","value":"attempted"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"QuestionCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Question"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"difficulty"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"QuestionSolvedStatus"}}]}}]} as unknown as DocumentNode<ListQuestionsQuery, ListQuestionsQueryVariables>;
16251635
export const CompletedQuestionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CompletedQuestions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"me"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"submissionStatistics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalQuestions"}},{"kind":"Field","name":{"kind":"Name","value":"solvedQuestions"}}]}}]}}]}}]} as unknown as DocumentNode<CompletedQuestionsQuery, CompletedQuestionsQueryVariables>;
1636+
export const MySolvedQuestionsCountDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MySolvedQuestionsCount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"me"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"submissionStatistics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"solvedQuestions"}}]}}]}}]}}]} as unknown as DocumentNode<MySolvedQuestionsCountQuery, MySolvedQuestionsCountQueryVariables>;
1637+
export const MyPointsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MyPoints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"me"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"totalPoints"}}]}}]}}]} as unknown as DocumentNode<MyPointsQuery, MyPointsQueryVariables>;
16261638
export const PointsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Points"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"me"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"totalPoints"}},{"kind":"Field","name":{"kind":"Name","value":"points"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"5"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"PointFragment"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PointFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Point"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"points"}}]}}]} as unknown as DocumentNode<PointsQuery, PointsQueryVariables>;
16271639
export const ResolvedQuestionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ResolvedQuestions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"me"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"submissionStatistics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalQuestions"}},{"kind":"Field","name":{"kind":"Name","value":"solvedQuestions"}}]}}]}}]}}]} as unknown as DocumentNode<ResolvedQuestionsQuery, ResolvedQuestionsQueryVariables>;
16281640
export const QuestionInfoDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"QuestionInfo"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"question"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"difficulty"}},{"kind":"Field","name":{"kind":"Name","value":"category"}}]}}]}}]} as unknown as DocumentNode<QuestionInfoQuery, QuestionInfoQueryVariables>;

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"react-remark": "^2.1.0",
5858
"remark": "^15.0.1",
5959
"remark-html": "^16.0.1",
60+
"shiki": "^3.13.0",
6061
"sonner": "^2.0.7",
6162
"sql-formatter": "^15.6.9",
6263
"streamdown": "^1.3.0",

pnpm-lock.yaml

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

0 commit comments

Comments
 (0)