Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions app/(app)/statistics/_components/ranking/completed-questions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"use client";

import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { graphql } from "@/gql";
import { useSuspenseQuery } from "@apollo/client/react";

const MY_SOLVED_QUESTIONS_COUNT = graphql(`
query MySolvedQuestionsCount {
me {
id
name
submissionStatistics {
solvedQuestions
}
}
}
`);

export default function SolvedQuestionsRanking() {
const { data } = useSuspenseQuery(MY_SOLVED_QUESTIONS_COUNT);

return (
<Table>
<TableHeader>
<TableRow>
<TableHead>姓名</TableHead>
<TableHead>解題數</TableHead>
</TableRow>
</TableHeader>

<TableBody>
<TableRow>
<TableCell>{data.me.name}</TableCell>
<TableCell>{data.me.submissionStatistics.solvedQuestions}</TableCell>
</TableRow>
</TableBody>
</Table>
);
}
39 changes: 39 additions & 0 deletions app/(app)/statistics/_components/ranking/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Skeleton } from "@/components/ui/skeleton";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Suspense } from "react";
import SolvedQuestionsRanking from "./completed-questions";
import PointsRanking from "./points";

export default function Ranking() {
return (
<Tabs defaultValue="daily-points">
<TabsList>
<TabsTrigger value="daily-points">每日 - 點數</TabsTrigger>
<TabsTrigger value="weekly-points">每週 - 點數</TabsTrigger>
<TabsTrigger value="daily-solved-questions">每日 - 做題數</TabsTrigger>
<TabsTrigger value="weekly-solved-questions">每週 - 做題數</TabsTrigger>
</TabsList>

<TabsContent value="daily-points">
<Suspense fallback={<Skeleton className="h-48" />}>
<PointsRanking />
</Suspense>
</TabsContent>
<TabsContent value="weekly-points">
<Suspense fallback={<Skeleton className="h-48" />}>
<PointsRanking />
</Suspense>
</TabsContent>
<TabsContent value="daily-solved-questions">
<Suspense fallback={<Skeleton className="h-48" />}>
<SolvedQuestionsRanking />
</Suspense>
</TabsContent>
<TabsContent value="weekly-solved-questions">
<Suspense fallback={<Skeleton className="h-48" />}>
<SolvedQuestionsRanking />
</Suspense>
</TabsContent>
</Tabs>
);
}
37 changes: 37 additions & 0 deletions app/(app)/statistics/_components/ranking/points.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use client";

import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { graphql } from "@/gql";
import { useSuspenseQuery } from "@apollo/client/react";

const MY_POINTS = graphql(`
query MyPoints {
me {
id
name
totalPoints
}
}
`);

export default function PointsRanking() {
const { data } = useSuspenseQuery(MY_POINTS);

return (
<Table>
<TableHeader>
<TableRow>
<TableHead>姓名</TableHead>
<TableHead>點數</TableHead>
</TableRow>
</TableHeader>

<TableBody>
<TableRow>
<TableCell>{data.me.name}</TableCell>
<TableCell>{data.me.totalPoints}</TableCell>
</TableRow>
</TableBody>
</Table>
);
}
12 changes: 2 additions & 10 deletions app/(app)/statistics/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Pickaxe } from "lucide-react";
import type { Metadata } from "next";
import { Suspense } from "react";
import Board from "./_components/board";
import Ranking from "./_components/ranking";
import Points from "./_components/statistics/points";
import ResolvedQuestions from "./_components/statistics/resolved-questions";

Expand Down Expand Up @@ -41,14 +40,7 @@ export default function StatisticsPage() {

<section>
<h2 className="mb-2 text-lg font-bold">排行榜</h2>

<Alert>
<Pickaxe />
<AlertTitle>正在實作</AlertTitle>
<AlertDescription>
功能正在實作,這裡先佔位!
</AlertDescription>
</Alert>
<Ranking />
</section>
</div>
</div>
Expand Down
12 changes: 12 additions & 0 deletions gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ type Documents = {
"\n query ChallengeStatistics {\n me {\n id\n submissionStatistics {\n totalQuestions\n solvedQuestions\n attemptedQuestions\n }\n }\n }\n": typeof types.ChallengeStatisticsDocument,
"\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,
"\n query CompletedQuestions {\n me {\n id\n submissionStatistics {\n totalQuestions\n solvedQuestions\n }\n }\n }\n": typeof types.CompletedQuestionsDocument,
"\n query MySolvedQuestionsCount {\n me {\n id\n name\n submissionStatistics {\n solvedQuestions\n }\n }\n }\n": typeof types.MySolvedQuestionsCountDocument,
"\n query MyPoints {\n me {\n id\n name\n totalPoints\n }\n }\n": typeof types.MyPointsDocument,
"\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,
"\n fragment PointFragment on Point {\n description\n points\n }\n": typeof types.PointFragmentFragmentDoc,
"\n query ResolvedQuestions {\n me {\n id\n submissionStatistics {\n totalQuestions\n solvedQuestions\n }\n }\n }\n": typeof types.ResolvedQuestionsDocument,
Expand All @@ -51,6 +53,8 @@ const documents: Documents = {
"\n query ChallengeStatistics {\n me {\n id\n submissionStatistics {\n totalQuestions\n solvedQuestions\n attemptedQuestions\n }\n }\n }\n": types.ChallengeStatisticsDocument,
"\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,
"\n query CompletedQuestions {\n me {\n id\n submissionStatistics {\n totalQuestions\n solvedQuestions\n }\n }\n }\n": types.CompletedQuestionsDocument,
"\n query MySolvedQuestionsCount {\n me {\n id\n name\n submissionStatistics {\n solvedQuestions\n }\n }\n }\n": types.MySolvedQuestionsCountDocument,
"\n query MyPoints {\n me {\n id\n name\n totalPoints\n }\n }\n": types.MyPointsDocument,
"\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,
"\n fragment PointFragment on Point {\n description\n points\n }\n": types.PointFragmentFragmentDoc,
"\n query ResolvedQuestions {\n me {\n id\n submissionStatistics {\n totalQuestions\n solvedQuestions\n }\n }\n }\n": types.ResolvedQuestionsDocument,
Expand Down Expand Up @@ -128,6 +132,14 @@ export function graphql(source: "\n query ListQuestions($where: QuestionWhereIn
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
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"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
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"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
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"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down
12 changes: 12 additions & 0 deletions gql/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1559,6 +1559,16 @@ export type CompletedQuestionsQueryVariables = Exact<{ [key: string]: never; }>;

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

export type MySolvedQuestionsCountQueryVariables = Exact<{ [key: string]: never; }>;


export type MySolvedQuestionsCountQuery = { __typename?: 'Query', me: { __typename?: 'User', id: string, name: string, submissionStatistics: { __typename?: 'SubmissionStatistics', solvedQuestions: number } } };

export type MyPointsQueryVariables = Exact<{ [key: string]: never; }>;


export type MyPointsQuery = { __typename?: 'Query', me: { __typename?: 'User', id: string, name: string, totalPoints: number } };

export type PointsQueryVariables = Exact<{ [key: string]: never; }>;


Expand Down Expand Up @@ -1623,6 +1633,8 @@ export const SubmissionHistoryDocument = {"kind":"Document","definitions":[{"kin
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>;
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>;
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>;
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>;
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>;
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>;
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>;
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>;
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"react-remark": "^2.1.0",
"remark": "^15.0.1",
"remark-html": "^16.0.1",
"shiki": "^3.13.0",
"sonner": "^2.0.7",
"sql-formatter": "^15.6.9",
"streamdown": "^1.3.0",
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.