Skip to content

Commit 7c6bd33

Browse files
committed
feat(question): implement pass rate
1 parent 6b73339 commit 7c6bd33

File tree

4 files changed

+125
-5
lines changed

4 files changed

+125
-5
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
"use client";
2+
3+
import { CardLayout } from "@/components/card-layout";
4+
import { type FragmentType, graphql, useFragment } from "@/gql";
5+
import { cn } from "@/lib/utils";
6+
7+
const QUESTION_PASS_RATE_FRAGMENT = graphql(`
8+
fragment QuestionPassRateCard on Question {
9+
statistics {
10+
passedUsers
11+
attemptedUsers
12+
submissionCount
13+
correctSubmissionCount
14+
}
15+
}
16+
`);
17+
18+
export function PassRateCard({ fragment }: { fragment: FragmentType<typeof QUESTION_PASS_RATE_FRAGMENT> }) {
19+
const { statistics } = useFragment(QUESTION_PASS_RATE_FRAGMENT, fragment);
20+
21+
const passRateValue = statistics.attemptedUsers > 0
22+
? (statistics.passedUsers / statistics.attemptedUsers) * 100
23+
: 0;
24+
25+
const passRate = passRateValue.toFixed(1);
26+
27+
const averageAttempts = statistics.attemptedUsers > 0
28+
? (statistics.submissionCount / statistics.attemptedUsers).toFixed(1)
29+
: "0";
30+
31+
const correctnessRateValue = statistics.submissionCount > 0
32+
? (statistics.correctSubmissionCount / statistics.submissionCount) * 100
33+
: 0;
34+
35+
const correctnessRate = correctnessRateValue.toFixed(1);
36+
37+
// Determine color based on pass rate thresholds
38+
const passRateColorClass = cn(
39+
"text-3xl font-bold",
40+
passRateValue > 80 && `
41+
text-green-600
42+
dark:text-green-400
43+
`,
44+
passRateValue > 40 && passRateValue <= 80 && `
45+
text-yellow-600
46+
dark:text-yellow-400
47+
`,
48+
passRateValue <= 40 && `
49+
text-red-600
50+
dark:text-red-400
51+
`,
52+
);
53+
54+
const correctnessRateColorClass = cn(
55+
"text-3xl font-bold",
56+
correctnessRateValue > 80 && `
57+
text-green-600
58+
dark:text-green-400
59+
`,
60+
correctnessRateValue > 40 && correctnessRateValue <= 80 && `
61+
text-yellow-600
62+
dark:text-yellow-400
63+
`,
64+
correctnessRateValue <= 40 && `
65+
text-red-600
66+
dark:text-red-400
67+
`,
68+
);
69+
70+
return (
71+
<CardLayout title="通過率統計" description="題目的通過率與答題情況統計">
72+
<div
73+
className={`
74+
grid grid-cols-1 gap-4
75+
md:grid-cols-3
76+
`}
77+
>
78+
<div className="rounded-lg border bg-muted/50 p-4">
79+
<p className="text-sm text-muted-foreground">通過率</p>
80+
<p className={passRateColorClass}>
81+
{passRate}%
82+
</p>
83+
<p className="mt-1 text-xs text-muted-foreground">
84+
{statistics.passedUsers} / {statistics.attemptedUsers} 人通過
85+
</p>
86+
</div>
87+
88+
<div className="rounded-lg border bg-muted/50 p-4">
89+
<p className="text-sm text-muted-foreground">平均答題次數</p>
90+
<p className="text-3xl font-bold">{averageAttempts}</p>
91+
<p className="mt-1 text-xs text-muted-foreground">
92+
{statistics.submissionCount} 次提交
93+
</p>
94+
</div>
95+
96+
<div className="rounded-lg border bg-muted/50 p-4">
97+
<p className="text-sm text-muted-foreground">正確率</p>
98+
<p className={correctnessRateColorClass}>
99+
{correctnessRate}%
100+
</p>
101+
<p className="mt-1 text-xs text-muted-foreground">
102+
{statistics.correctSubmissionCount} / {statistics.submissionCount} 次正確
103+
</p>
104+
</div>
105+
</div>
106+
</CardLayout>
107+
);
108+
}

app/(admin)/(question-management)/questions/[id]/_components/question-cards.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useSuspenseQuery } from "@apollo/client/react";
55
import { AnswerCard } from "./answer-card";
66
import { DatabaseCard } from "./database-card";
77
import { DescriptionCard } from "./description-card";
8+
import { PassRateCard } from "./pass-rate";
89

910
const QUESTION_CARDS_QUERY = graphql(`
1011
query QuestionCards($id: ID!) {
@@ -13,6 +14,7 @@ const QUESTION_CARDS_QUERY = graphql(`
1314
...QuestionDescriptionCard
1415
...QuestionDatabaseCard
1516
...QuestionAnswerCard
17+
...QuestionPassRateCard
1618
}
1719
}
1820
`);
@@ -34,6 +36,7 @@ export function QuestionCards({ id }: { id: string }) {
3436
<DescriptionCard fragment={fragment} />
3537
<DatabaseCard fragment={fragment} />
3638
<AnswerCard fragment={fragment} />
39+
<PassRateCard fragment={fragment} />
3740
</div>
3841
);
3942
}

gql/gql.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ type Documents = {
4141
"\n fragment QuestionDatabaseCard on Question {\n database {\n id\n slug\n description\n }\n }\n": typeof types.QuestionDatabaseCardFragmentDoc,
4242
"\n fragment QuestionDescriptionCard on Question {\n description\n }\n": typeof types.QuestionDescriptionCardFragmentDoc,
4343
"\n query QuestionHeader($id: ID!) {\n question(id: $id) {\n id\n title\n description\n category\n difficulty\n }\n }\n": typeof types.QuestionHeaderDocument,
44-
"\n query QuestionCards($id: ID!) {\n question(id: $id) {\n id\n ...QuestionDescriptionCard\n ...QuestionDatabaseCard\n ...QuestionAnswerCard\n }\n }\n": typeof types.QuestionCardsDocument,
44+
"\n fragment QuestionPassRateCard on Question {\n statistics {\n passedUsers\n attemptedUsers\n submissionCount\n correctSubmissionCount\n }\n }\n": typeof types.QuestionPassRateCardFragmentDoc,
45+
"\n query QuestionCards($id: ID!) {\n question(id: $id) {\n id\n ...QuestionDescriptionCard\n ...QuestionDatabaseCard\n ...QuestionAnswerCard\n ...QuestionPassRateCard\n }\n }\n": typeof types.QuestionCardsDocument,
4546
"\n query QuestionReferenceAnswerResult($id: ID!) {\n question(id: $id) {\n id\n referenceAnswerResult {\n columns\n rows\n }\n }\n }\n": typeof types.QuestionReferenceAnswerResultDocument,
4647
"\n query CreateQuestionDialogContent {\n ...QuestionUpdateForm\n }\n": typeof types.CreateQuestionDialogContentDocument,
4748
"\n mutation CreateQuestion($input: CreateQuestionInput!) {\n createQuestion(input: $input) {\n id\n }\n }\n": typeof types.CreateQuestionDocument,
@@ -122,7 +123,8 @@ const documents: Documents = {
122123
"\n fragment QuestionDatabaseCard on Question {\n database {\n id\n slug\n description\n }\n }\n": types.QuestionDatabaseCardFragmentDoc,
123124
"\n fragment QuestionDescriptionCard on Question {\n description\n }\n": types.QuestionDescriptionCardFragmentDoc,
124125
"\n query QuestionHeader($id: ID!) {\n question(id: $id) {\n id\n title\n description\n category\n difficulty\n }\n }\n": types.QuestionHeaderDocument,
125-
"\n query QuestionCards($id: ID!) {\n question(id: $id) {\n id\n ...QuestionDescriptionCard\n ...QuestionDatabaseCard\n ...QuestionAnswerCard\n }\n }\n": types.QuestionCardsDocument,
126+
"\n fragment QuestionPassRateCard on Question {\n statistics {\n passedUsers\n attemptedUsers\n submissionCount\n correctSubmissionCount\n }\n }\n": types.QuestionPassRateCardFragmentDoc,
127+
"\n query QuestionCards($id: ID!) {\n question(id: $id) {\n id\n ...QuestionDescriptionCard\n ...QuestionDatabaseCard\n ...QuestionAnswerCard\n ...QuestionPassRateCard\n }\n }\n": types.QuestionCardsDocument,
126128
"\n query QuestionReferenceAnswerResult($id: ID!) {\n question(id: $id) {\n id\n referenceAnswerResult {\n columns\n rows\n }\n }\n }\n": types.QuestionReferenceAnswerResultDocument,
127129
"\n query CreateQuestionDialogContent {\n ...QuestionUpdateForm\n }\n": types.CreateQuestionDialogContentDocument,
128130
"\n mutation CreateQuestion($input: CreateQuestionInput!) {\n createQuestion(input: $input) {\n id\n }\n }\n": types.CreateQuestionDocument,
@@ -301,7 +303,11 @@ export function graphql(source: "\n query QuestionHeader($id: ID!) {\n quest
301303
/**
302304
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
303305
*/
304-
export function graphql(source: "\n query QuestionCards($id: ID!) {\n question(id: $id) {\n id\n ...QuestionDescriptionCard\n ...QuestionDatabaseCard\n ...QuestionAnswerCard\n }\n }\n"): (typeof documents)["\n query QuestionCards($id: ID!) {\n question(id: $id) {\n id\n ...QuestionDescriptionCard\n ...QuestionDatabaseCard\n ...QuestionAnswerCard\n }\n }\n"];
306+
export function graphql(source: "\n fragment QuestionPassRateCard on Question {\n statistics {\n passedUsers\n attemptedUsers\n submissionCount\n correctSubmissionCount\n }\n }\n"): (typeof documents)["\n fragment QuestionPassRateCard on Question {\n statistics {\n passedUsers\n attemptedUsers\n submissionCount\n correctSubmissionCount\n }\n }\n"];
307+
/**
308+
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
309+
*/
310+
export function graphql(source: "\n query QuestionCards($id: ID!) {\n question(id: $id) {\n id\n ...QuestionDescriptionCard\n ...QuestionDatabaseCard\n ...QuestionAnswerCard\n ...QuestionPassRateCard\n }\n }\n"): (typeof documents)["\n query QuestionCards($id: ID!) {\n question(id: $id) {\n id\n ...QuestionDescriptionCard\n ...QuestionDatabaseCard\n ...QuestionAnswerCard\n ...QuestionPassRateCard\n }\n }\n"];
305311
/**
306312
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
307313
*/

0 commit comments

Comments
 (0)