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
50 changes: 47 additions & 3 deletions app/(app)/challenges/[id]/_components/header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"use client";

import ColoredRate from "@/components/colored-rate";
import DifficultyBadge from "@/components/question/difficulty-badge";
import SolvedStatusBadge from "@/components/question/solved-status-badge";
import { Badge } from "@/components/ui/badge";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { graphql } from "@/gql";
import { getQuestionSolvedStatus } from "@/lib/solved-status";
import { useSuspenseQuery } from "@apollo/client/react";
Expand All @@ -15,29 +17,71 @@ export const QUESTION_HEADER = graphql(`
difficulty
category
statistics {
passedUsers
attemptedUsers
correctSubmissionCount
submissionCount
}
...QuestionSolvedStatus
}
}
`);

export default function Header({ id }: { id: string }) {
const { data } = useSuspenseQuery(QUESTION_HEADER, { variables: { id } });
const { title, difficulty, category } = data.question;
const { title, difficulty, category, statistics } = data.question;

const passedRate = statistics.attemptedUsers ? statistics.passedUsers / statistics.attemptedUsers : 0;
const correctSubmissionRate = statistics.submissionCount
? statistics.correctSubmissionCount / statistics.submissionCount
: 0;

const solvedStatus = getQuestionSolvedStatus(data.question);

return (
<div>
{/* Header */}
<header className="mb-6 flex items-center gap-6">
<div className="text-xl leading-none font-bold tracking-wide">{title}</div>
<div className="h-4 w-px bg-border" />
<div className="text-xl leading-none font-bold tracking-wide">
{title}
</div>
<Separator />
<div className="flex items-center gap-2">
<Badge>{category}</Badge>
<DifficultyBadge difficulty={difficulty} />
<SolvedStatusBadge solvedStatus={solvedStatus} />
</div>
<Separator />
<div className="flex items-center gap-2">
<Tooltip>
<TooltipTrigger>
<span className="text-sm text-muted-foreground">
通過率 <ColoredRate rate={passedRate} />
</span>
</TooltipTrigger>
<TooltipContent>
共有 {statistics.attemptedUsers} 人嘗試這題,其中 {statistics.passedUsers} 人通過。
</TooltipContent>
</Tooltip>

<Tooltip>
<TooltipTrigger>
<span className="text-sm text-muted-foreground">
正確率 <ColoredRate rate={correctSubmissionRate} />
</span>
</TooltipTrigger>
<TooltipContent>
共有 {statistics.submissionCount} 次提交,其中 {statistics.correctSubmissionCount} 次正確。
</TooltipContent>
</Tooltip>
</div>
</header>
</div>
);
}

function Separator() {
return <div className="h-4 w-px bg-border" />;
}
24 changes: 24 additions & 0 deletions components/colored-rate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { cn } from "@/lib/utils";

export interface ColoredRateProps extends React.ComponentPropsWithoutRef<"span"> {
rate: number; // float
}

export default function ColoredRate({
rate,
className,
...props
}: ColoredRateProps) {
const color = rate > 0.8
? "text-green-800"
: rate > 0.5
? "text-yellow-800"
: "text-red-800";
const roundedRate = Math.round(rate * 100);

return (
<span className={cn(color, className)} {...props}>
{roundedRate}%
</span>
);
}
13 changes: 13 additions & 0 deletions components/question/question-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getQuestionSolvedStatus } from "@/lib/solved-status";
import { SwordIcon } from "lucide-react";
import Link from "next/link";
import { Remark } from "react-remark";
import ColoredRate from "../colored-rate";
import DifficultyBadge from "./difficulty-badge";
import SolvedStatusBadge from "./solved-status-badge";

Expand All @@ -15,6 +16,11 @@ const QUESTION_CARD_FRAGMENT = graphql(`
difficulty
category

statistics {
passedUsers
attemptedUsers
}

...QuestionSolvedStatus
}
`);
Expand All @@ -28,6 +34,10 @@ export default function QuestionCard({
const descriptionFirstLine = question.description.split("\n")[0];
const solvedStatus = getQuestionSolvedStatus(question);

const passedRate = question.statistics.attemptedUsers
? question.statistics.passedUsers / question.statistics.attemptedUsers
: 0;

return (
<article className="flex overflow-hidden rounded">
{/* Question Body */}
Expand All @@ -42,6 +52,9 @@ export default function QuestionCard({
<SolvedStatusBadge solvedStatus={solvedStatus} />
<DifficultyBadge difficulty={question.difficulty} />
<Badge>{question.category}</Badge>
<Badge variant="outline">
通過率 <ColoredRate rate={passedRate} />
</Badge>
</div>
</div>

Expand Down
12 changes: 6 additions & 6 deletions gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-
* Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
*/
type Documents = {
"\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,
"\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,
"\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,
"\n query CorrectAnswer($id: ID!) {\n question(id: $id) {\n id\n referenceAnswerResult {\n columns\n rows\n }\n }\n }\n": typeof types.CorrectAnswerDocument,
"\n query DatabaseRelationship($id: ID!) {\n question(id: $id) {\n database {\n id\n slug\n relationFigure\n }\n }\n }\n": typeof types.DatabaseRelationshipDocument,
Expand All @@ -40,12 +40,12 @@ type Documents = {
"\n fragment QuestionInfoFragment on Question {\n id\n title\n description\n difficulty\n category\n }\n": typeof types.QuestionInfoFragmentFragmentDoc,
"\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,
"\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,
"\n fragment QuestionCard on Question {\n id\n title\n description\n difficulty\n category\n\n ...QuestionSolvedStatus\n }\n": typeof types.QuestionCardFragmentDoc,
"\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,
"\n fragment QuestionSolvedStatus on Question {\n solved\n attempted\n }\n": typeof types.QuestionSolvedStatusFragmentDoc,
"\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,
};
const documents: Documents = {
"\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,
"\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,
"\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,
"\n query CorrectAnswer($id: ID!) {\n question(id: $id) {\n id\n referenceAnswerResult {\n columns\n rows\n }\n }\n }\n": types.CorrectAnswerDocument,
"\n query DatabaseRelationship($id: ID!) {\n question(id: $id) {\n database {\n id\n slug\n relationFigure\n }\n }\n }\n": types.DatabaseRelationshipDocument,
Expand All @@ -71,7 +71,7 @@ const documents: Documents = {
"\n fragment QuestionInfoFragment on Question {\n id\n title\n description\n difficulty\n category\n }\n": types.QuestionInfoFragmentFragmentDoc,
"\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,
"\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,
"\n fragment QuestionCard on Question {\n id\n title\n description\n difficulty\n category\n\n ...QuestionSolvedStatus\n }\n": types.QuestionCardFragmentDoc,
"\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,
"\n fragment QuestionSolvedStatus on Question {\n solved\n attempted\n }\n": types.QuestionSolvedStatusFragmentDoc,
"\n query BasicUserInfo {\n me {\n id\n name\n email\n avatar\n\n group {\n name\n }\n }\n }\n": types.BasicUserInfoDocument,
};
Expand All @@ -93,7 +93,7 @@ export function graphql(source: string): unknown;
/**
* 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 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"];
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"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down Expand Up @@ -197,7 +197,7 @@ export function graphql(source: "\n query QuestionSchema($id: ID!) {\n quest
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
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"];
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"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down
Loading