Skip to content

Commit b0690ef

Browse files
authored
Merge pull request #31 from database-playground/pan93412/dbp-100-顯示題目做題動態
feat(challenges): implement pass rate
2 parents d2acf19 + 3d2c506 commit b0690ef

File tree

6 files changed

+122
-14
lines changed

6 files changed

+122
-14
lines changed

app/(app)/challenges/[id]/_components/header/index.tsx

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
"use client";
22

3+
import ColoredRate from "@/components/colored-rate";
34
import DifficultyBadge from "@/components/question/difficulty-badge";
45
import SolvedStatusBadge from "@/components/question/solved-status-badge";
56
import { Badge } from "@/components/ui/badge";
7+
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
68
import { graphql } from "@/gql";
79
import { getQuestionSolvedStatus } from "@/lib/solved-status";
810
import { useSuspenseQuery } from "@apollo/client/react";
@@ -15,29 +17,71 @@ export const QUESTION_HEADER = graphql(`
1517
difficulty
1618
category
1719
20+
statistics {
21+
passedUsers
22+
attemptedUsers
23+
correctSubmissionCount
24+
submissionCount
25+
}
26+
1827
...QuestionSolvedStatus
1928
}
2029
}
2130
`);
2231

2332
export default function Header({ id }: { id: string }) {
2433
const { data } = useSuspenseQuery(QUESTION_HEADER, { variables: { id } });
25-
const { title, difficulty, category } = data.question;
34+
const { title, difficulty, category, statistics } = data.question;
35+
36+
const passedRate = statistics.attemptedUsers ? statistics.passedUsers / statistics.attemptedUsers : 0;
37+
const correctSubmissionRate = statistics.submissionCount
38+
? statistics.correctSubmissionCount / statistics.submissionCount
39+
: 0;
2640

2741
const solvedStatus = getQuestionSolvedStatus(data.question);
2842

2943
return (
3044
<div>
3145
{/* Header */}
3246
<header className="mb-6 flex items-center gap-6">
33-
<div className="text-xl leading-none font-bold tracking-wide">{title}</div>
34-
<div className="h-4 w-px bg-border" />
47+
<div className="text-xl leading-none font-bold tracking-wide">
48+
{title}
49+
</div>
50+
<Separator />
3551
<div className="flex items-center gap-2">
3652
<Badge>{category}</Badge>
3753
<DifficultyBadge difficulty={difficulty} />
3854
<SolvedStatusBadge solvedStatus={solvedStatus} />
3955
</div>
56+
<Separator />
57+
<div className="flex items-center gap-2">
58+
<Tooltip>
59+
<TooltipTrigger>
60+
<span className="text-sm text-muted-foreground">
61+
通過率 <ColoredRate rate={passedRate} />
62+
</span>
63+
</TooltipTrigger>
64+
<TooltipContent>
65+
共有 {statistics.attemptedUsers} 人嘗試這題,其中 {statistics.passedUsers} 人通過。
66+
</TooltipContent>
67+
</Tooltip>
68+
69+
<Tooltip>
70+
<TooltipTrigger>
71+
<span className="text-sm text-muted-foreground">
72+
正確率 <ColoredRate rate={correctSubmissionRate} />
73+
</span>
74+
</TooltipTrigger>
75+
<TooltipContent>
76+
共有 {statistics.submissionCount} 次提交,其中 {statistics.correctSubmissionCount} 次正確。
77+
</TooltipContent>
78+
</Tooltip>
79+
</div>
4080
</header>
4181
</div>
4282
);
4383
}
84+
85+
function Separator() {
86+
return <div className="h-4 w-px bg-border" />;
87+
}

components/colored-rate.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { cn } from "@/lib/utils";
2+
3+
export interface ColoredRateProps extends React.ComponentPropsWithoutRef<"span"> {
4+
rate: number; // float
5+
}
6+
7+
export default function ColoredRate({
8+
rate,
9+
className,
10+
...props
11+
}: ColoredRateProps) {
12+
const color = rate > 0.8
13+
? "text-green-800"
14+
: rate > 0.5
15+
? "text-yellow-800"
16+
: "text-red-800";
17+
const roundedRate = Math.round(rate * 100);
18+
19+
return (
20+
<span className={cn(color, className)} {...props}>
21+
{roundedRate}%
22+
</span>
23+
);
24+
}

components/question/question-card.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { getQuestionSolvedStatus } from "@/lib/solved-status";
44
import { SwordIcon } from "lucide-react";
55
import Link from "next/link";
66
import { Remark } from "react-remark";
7+
import ColoredRate from "../colored-rate";
78
import DifficultyBadge from "./difficulty-badge";
89
import SolvedStatusBadge from "./solved-status-badge";
910

@@ -15,6 +16,11 @@ const QUESTION_CARD_FRAGMENT = graphql(`
1516
difficulty
1617
category
1718
19+
statistics {
20+
passedUsers
21+
attemptedUsers
22+
}
23+
1824
...QuestionSolvedStatus
1925
}
2026
`);
@@ -28,6 +34,10 @@ export default function QuestionCard({
2834
const descriptionFirstLine = question.description.split("\n")[0];
2935
const solvedStatus = getQuestionSolvedStatus(question);
3036

37+
const passedRate = question.statistics.attemptedUsers
38+
? question.statistics.passedUsers / question.statistics.attemptedUsers
39+
: 0;
40+
3141
return (
3242
<article className="flex overflow-hidden rounded">
3343
{/* Question Body */}
@@ -42,6 +52,9 @@ export default function QuestionCard({
4252
<SolvedStatusBadge solvedStatus={solvedStatus} />
4353
<DifficultyBadge difficulty={question.difficulty} />
4454
<Badge>{question.category}</Badge>
55+
<Badge variant="outline">
56+
通過率 <ColoredRate rate={passedRate} />
57+
</Badge>
4558
</div>
4659
</div>
4760

gql/gql.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-
1414
* Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
1515
*/
1616
type Documents = {
17-
"\n query QuestionHeader($id: ID!) {\n question(id: $id) {\n id\n title\n difficulty\n category\n\n ...QuestionSolvedStatus\n }\n }\n": typeof types.QuestionHeaderDocument,
17+
"\n query QuestionHeader($id: ID!) {\n question(id: $id) {\n id\n title\n difficulty\n category\n\n statistics {\n passedUsers\n attemptedUsers\n correctSubmissionCount\n submissionCount\n }\n\n ...QuestionSolvedStatus\n }\n }\n": typeof types.QuestionHeaderDocument,
1818
"\n query CompareAnswer($id: ID!) {\n question(id: $id) {\n id\n referenceAnswerResult {\n columns\n rows\n }\n lastSubmission {\n id\n status\n queryResult {\n columns\n rows\n }\n error\n }\n }\n }\n": typeof types.CompareAnswerDocument,
1919
"\n query CorrectAnswer($id: ID!) {\n question(id: $id) {\n id\n referenceAnswerResult {\n columns\n rows\n }\n }\n }\n": typeof types.CorrectAnswerDocument,
2020
"\n query DatabaseRelationship($id: ID!) {\n question(id: $id) {\n database {\n id\n slug\n relationFigure\n }\n }\n }\n": typeof types.DatabaseRelationshipDocument,
@@ -40,12 +40,12 @@ type Documents = {
4040
"\n fragment QuestionInfoFragment on Question {\n id\n title\n description\n difficulty\n category\n }\n": typeof types.QuestionInfoFragmentFragmentDoc,
4141
"\n query UserAnswerResult($id: ID!) {\n question(id: $id) {\n id\n lastSubmission {\n id\n submittedCode\n status\n queryResult {\n columns\n rows\n }\n error\n }\n }\n }\n": typeof types.UserAnswerResultDocument,
4242
"\n query QuestionSchema($id: ID!) {\n question(id: $id) {\n id\n database {\n id\n structure {\n tables {\n columns\n name\n }\n }\n }\n }\n }": typeof types.QuestionSchemaDocument,
43-
"\n fragment QuestionCard on Question {\n id\n title\n description\n difficulty\n category\n\n ...QuestionSolvedStatus\n }\n": typeof types.QuestionCardFragmentDoc,
43+
"\n fragment QuestionCard on Question {\n id\n title\n description\n difficulty\n category\n\n statistics {\n passedUsers\n attemptedUsers\n }\n\n ...QuestionSolvedStatus\n }\n": typeof types.QuestionCardFragmentDoc,
4444
"\n fragment QuestionSolvedStatus on Question {\n solved\n attempted\n }\n": typeof types.QuestionSolvedStatusFragmentDoc,
4545
"\n query BasicUserInfo {\n me {\n id\n name\n email\n avatar\n\n group {\n name\n }\n }\n }\n": typeof types.BasicUserInfoDocument,
4646
};
4747
const documents: Documents = {
48-
"\n query QuestionHeader($id: ID!) {\n question(id: $id) {\n id\n title\n difficulty\n category\n\n ...QuestionSolvedStatus\n }\n }\n": types.QuestionHeaderDocument,
48+
"\n query QuestionHeader($id: ID!) {\n question(id: $id) {\n id\n title\n difficulty\n category\n\n statistics {\n passedUsers\n attemptedUsers\n correctSubmissionCount\n submissionCount\n }\n\n ...QuestionSolvedStatus\n }\n }\n": types.QuestionHeaderDocument,
4949
"\n query CompareAnswer($id: ID!) {\n question(id: $id) {\n id\n referenceAnswerResult {\n columns\n rows\n }\n lastSubmission {\n id\n status\n queryResult {\n columns\n rows\n }\n error\n }\n }\n }\n": types.CompareAnswerDocument,
5050
"\n query CorrectAnswer($id: ID!) {\n question(id: $id) {\n id\n referenceAnswerResult {\n columns\n rows\n }\n }\n }\n": types.CorrectAnswerDocument,
5151
"\n query DatabaseRelationship($id: ID!) {\n question(id: $id) {\n database {\n id\n slug\n relationFigure\n }\n }\n }\n": types.DatabaseRelationshipDocument,
@@ -71,7 +71,7 @@ const documents: Documents = {
7171
"\n fragment QuestionInfoFragment on Question {\n id\n title\n description\n difficulty\n category\n }\n": types.QuestionInfoFragmentFragmentDoc,
7272
"\n query UserAnswerResult($id: ID!) {\n question(id: $id) {\n id\n lastSubmission {\n id\n submittedCode\n status\n queryResult {\n columns\n rows\n }\n error\n }\n }\n }\n": types.UserAnswerResultDocument,
7373
"\n query QuestionSchema($id: ID!) {\n question(id: $id) {\n id\n database {\n id\n structure {\n tables {\n columns\n name\n }\n }\n }\n }\n }": types.QuestionSchemaDocument,
74-
"\n fragment QuestionCard on Question {\n id\n title\n description\n difficulty\n category\n\n ...QuestionSolvedStatus\n }\n": types.QuestionCardFragmentDoc,
74+
"\n fragment QuestionCard on Question {\n id\n title\n description\n difficulty\n category\n\n statistics {\n passedUsers\n attemptedUsers\n }\n\n ...QuestionSolvedStatus\n }\n": types.QuestionCardFragmentDoc,
7575
"\n fragment QuestionSolvedStatus on Question {\n solved\n attempted\n }\n": types.QuestionSolvedStatusFragmentDoc,
7676
"\n query BasicUserInfo {\n me {\n id\n name\n email\n avatar\n\n group {\n name\n }\n }\n }\n": types.BasicUserInfoDocument,
7777
};
@@ -93,7 +93,7 @@ export function graphql(source: string): unknown;
9393
/**
9494
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
9595
*/
96-
export function graphql(source: "\n query QuestionHeader($id: ID!) {\n question(id: $id) {\n id\n title\n difficulty\n category\n\n ...QuestionSolvedStatus\n }\n }\n"): (typeof documents)["\n query QuestionHeader($id: ID!) {\n question(id: $id) {\n id\n title\n difficulty\n category\n\n ...QuestionSolvedStatus\n }\n }\n"];
96+
export function graphql(source: "\n query QuestionHeader($id: ID!) {\n question(id: $id) {\n id\n title\n difficulty\n category\n\n statistics {\n passedUsers\n attemptedUsers\n correctSubmissionCount\n submissionCount\n }\n\n ...QuestionSolvedStatus\n }\n }\n"): (typeof documents)["\n query QuestionHeader($id: ID!) {\n question(id: $id) {\n id\n title\n difficulty\n category\n\n statistics {\n passedUsers\n attemptedUsers\n correctSubmissionCount\n submissionCount\n }\n\n ...QuestionSolvedStatus\n }\n }\n"];
9797
/**
9898
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
9999
*/
@@ -197,7 +197,7 @@ export function graphql(source: "\n query QuestionSchema($id: ID!) {\n quest
197197
/**
198198
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
199199
*/
200-
export function graphql(source: "\n fragment QuestionCard on Question {\n id\n title\n description\n difficulty\n category\n\n ...QuestionSolvedStatus\n }\n"): (typeof documents)["\n fragment QuestionCard on Question {\n id\n title\n description\n difficulty\n category\n\n ...QuestionSolvedStatus\n }\n"];
200+
export function graphql(source: "\n fragment QuestionCard on Question {\n id\n title\n description\n difficulty\n category\n\n statistics {\n passedUsers\n attemptedUsers\n }\n\n ...QuestionSolvedStatus\n }\n"): (typeof documents)["\n fragment QuestionCard on Question {\n id\n title\n description\n difficulty\n category\n\n statistics {\n passedUsers\n attemptedUsers\n }\n\n ...QuestionSolvedStatus\n }\n"];
201201
/**
202202
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
203203
*/

0 commit comments

Comments
 (0)