Skip to content

Commit

Permalink
feat(client): refine leaderboard (#582)
Browse files Browse the repository at this point in the history
* fix ts

* add most liked option to the quizzes
  • Loading branch information
umitcan07 authored Oct 21, 2024
1 parent caff62a commit e80b560
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 132 deletions.
2 changes: 1 addition & 1 deletion client/src/routes/Leaderboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export const Leaderboard = () => {
/>

<div className="flex gap-2">
{["quiz", "forum"].map((option) => (
{(["quiz", "forum"] as const).map((option) => (
<label
key={option}
className="flex cursor-pointer items-center gap-2"
Expand Down
37 changes: 3 additions & 34 deletions client/src/routes/Quiz.data.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,8 @@
import { LoaderFunction } from "react-router";
import { array, object, safeParse, string } from "valibot";
import { safeParse } from "valibot";
import { quizDetailsSchema } from "../types/quiz";
import { BASE_URL } from "../utils";

const quizSchema = object({
id: string(),
title: string(),
description: string(),
author: object({
full_name: string(),
username: string(),
avatar: string(),
}),
created_at: string(),
tags: array(
object({
id: string(),
name: string(),
}),
),
questions: array(
object({
id: string(),
text: string(),
options: array(
object({
id: string(),
text: string(),
is_correct: string(),
}),
),
selected_option_id: string(),
}),
),
});

export const quizLoader = (async ({ params }) => {
const { quizId } = params;

Expand All @@ -54,7 +23,7 @@ export const quizLoader = (async ({ params }) => {

const data = await res.json();
console.log(data);
const { output, issues, success } = safeParse(quizSchema, data);
const { output, issues, success } = safeParse(quizDetailsSchema, data);
if (!success) {
throw new Error(`Failed to parse quiz response: ${issues}`);
}
Expand Down
6 changes: 3 additions & 3 deletions client/src/routes/Quiz.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { useEffect, useState } from "react";
import { useLoaderData } from "react-router-typesafe";
import { buttonClass, buttonInnerRing } from "../components/button";
import { PageHead } from "../components/page-head";
import { QuizDetails } from "../types/quiz";
import { quizLoader } from "./Quiz.data";
import { Quiz } from "./Quizzes.data";

const StartQuizComponent = ({
quiz,
onStart,
}: {
quiz: Quiz & { questions: { id: number; text: string }[] };
quiz: QuizDetails;
onStart: () => void;
}) => (
<div
Expand Down Expand Up @@ -43,7 +43,7 @@ const StartQuizComponent = ({

export const QuizPage = () => {
const [currentQuestion, setCurrentQuestion] = useState(0);
const [selectedOption, setSelectedOption] = useState("");
const [_, setSelectedOption] = useState("");
const quiz = useLoaderData<typeof quizLoader>();
const [answers, setAnswers] = useState<Record<number, string>>({});
const [isQuizStarted, setIsQuizStarted] = useState(false);
Expand Down
53 changes: 29 additions & 24 deletions client/src/routes/Quizzes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export const Quizzes = () => {
);
} else if (sortBy === "popular") {
return b.num_taken - a.num_taken;
} else if (sortBy === "most liked") {
return b.rating.score - a.rating.score;
}
return 0;
});
Expand Down Expand Up @@ -97,31 +99,34 @@ export const Quizzes = () => {
</div>
</div>
<div className="flex gap-2">
{["newest", "oldest", "popular"].map((option) => (
<label
key={option}
className="flex cursor-pointer items-center gap-2"
>
<input
type="radio"
value={option}
checked={sortBy === option}
onChange={(e) => setSortBy(e.target.value)}
className="sr-only"
/>
<span
className={`rounded-full px-4 py-1.5 font-medium transition-all ${
sortBy === option
? "bg-cyan-900 text-white"
: "bg-slate-100 text-slate-900 hover:bg-slate-200"
}`}
{["newest", "oldest", "popular", "most liked"].map(
(option) => (
<label
key={option}
className="flex cursor-pointer items-center gap-2"
>
{option === "newest" && "Newest"}
{option === "oldest" && "Oldest"}
{option === "popular" && "Most Popular"}
</span>
</label>
))}
<input
type="radio"
value={option}
checked={sortBy === option}
onChange={(e) => setSortBy(e.target.value)}
className="sr-only"
/>
<span
className={`rounded-full px-4 py-1.5 font-medium transition-all ${
sortBy === option
? "bg-cyan-900 text-white"
: "bg-slate-100 text-slate-900 hover:bg-slate-200"
}`}
>
{option === "newest" && "Newest"}
{option === "oldest" && "Oldest"}
{option === "popular" && "Most Popular"}
{option === "most liked" && "Most Liked"}
</span>
</label>
),
)}
</div>
</aside>
<main className="grid grid-cols-1 items-stretch justify-stretch gap-6 sm:grid-cols-2 lg:grid-cols-3">
Expand Down
48 changes: 21 additions & 27 deletions client/src/types/quiz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,6 @@ import {
union,
} from "valibot";

// const quizSchema = object({
// id: string(),
// title: string(),
// description: string(),
// author: object({
// full_name: string(),
// username: string(),
// avatar: string(),
// }),
// created_at: string(),
// tags: array(
// object({
// id: string(),
// name: string(),
// }),
// ),
// type: number(),
// num_taken: number(),
// is_taken: boolean(),
// question_count: number(),
// difficulty: string(),
// rating: object({
// score: number(),
// count: number(),
// }),
// });

export const quizAuthorSchema = object({
full_name: string(),
username: string(),
Expand Down Expand Up @@ -75,4 +48,25 @@ export const quizOverviewSchema = object({
rating: ratingSchema,
});

export const questionsSchema = array(
object({
id: string(),
text: string(),
options: array(
object({
id: string(),
text: string(),
is_correct: string(),
}),
),
selected_option_id: string(),
}),
);

export const quizDetailsSchema = object({
...quizOverviewSchema.entries,
questions: questionsSchema,
});

export type QuizOverview = InferInput<typeof quizOverviewSchema>;
export type QuizDetails = InferInput<typeof quizDetailsSchema>;
1 change: 0 additions & 1 deletion client/src/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ export const getRelativeTime = (
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
const weeks = Math.floor(days / 7);

if (seconds < 60) return rtf.format(-seconds, "second");
if (minutes < 60) return rtf.format(-minutes, "minute");
Expand Down
43 changes: 1 addition & 42 deletions client/tsconfig.app.tsbuildinfo
Original file line number Diff line number Diff line change
@@ -1,42 +1 @@
{
"root": [
"./src/imagelink.tsx",
"./src/constants.ts",
"./src/hooks.ts",
"./src/main.tsx",
"./src/router.tsx",
"./src/store.ts",
"./src/utils.tsx",
"./src/vite-env.d.ts",
"./src/components/button.tsx",
"./src/components/input.tsx",
"./src/components/logo.tsx",
"./src/components/navbar.tsx",
"./src/components/sprite.tsx",
"./src/components/toast.tsx",
"./src/mocks/browser.ts",
"./src/mocks/handlers.auth.ts",
"./src/mocks/handlers.quiz.ts",
"./src/mocks/mocks.auth.ts",
"./src/mocks/mocks.quiz.ts",
"./src/routes/home.data.tsx",
"./src/routes/home.tsx",
"./src/routes/id.tsx",
"./src/routes/leaderboard.data.tsx",
"./src/routes/leaderboard.tsx",
"./src/routes/login.data.ts",
"./src/routes/login.tsx",
"./src/routes/logout.data.tsx",
"./src/routes/quiz.data.tsx",
"./src/routes/quiz.tsx",
"./src/routes/quizzes.data.tsx",
"./src/routes/quizzes.tsx",
"./src/routes/register.data.ts",
"./src/routes/register.tsx",
"./src/routes/_error.tsx",
"./src/routes/_root.tsx",
"./src/types/quiz.ts",
"./src/types/user.ts"
],
"version": "5.6.3"
}
{"root":["./src/imagelink.tsx","./src/constants.ts","./src/hooks.ts","./src/main.tsx","./src/router.tsx","./src/store.ts","./src/utils.tsx","./src/vite-env.d.ts","./src/components/avatar.tsx","./src/components/button.tsx","./src/components/input.tsx","./src/components/logo.tsx","./src/components/navbar.tsx","./src/components/page-head.tsx","./src/components/quiz-card.tsx","./src/components/score.tsx","./src/components/sprite.tsx","./src/components/toast.tsx","./src/mocks/browser.ts","./src/mocks/handlers.auth.ts","./src/mocks/handlers.quiz.ts","./src/mocks/mocks.auth.ts","./src/mocks/mocks.quiz.ts","./src/routes/forum.tsx","./src/routes/home.data.tsx","./src/routes/home.main.tsx","./src/routes/home.tsx","./src/routes/id.tsx","./src/routes/leaderboard.data.tsx","./src/routes/leaderboard.tsx","./src/routes/login.data.ts","./src/routes/login.tsx","./src/routes/logout.data.tsx","./src/routes/quiz.data.tsx","./src/routes/quiz.tsx","./src/routes/quizzes.data.tsx","./src/routes/quizzes.tsx","./src/routes/register.data.ts","./src/routes/register.tsx","./src/routes/_error.tsx","./src/routes/_root.tsx","./src/types/mockterm.ts","./src/types/quiz.ts","./src/types/quizquestion.ts","./src/types/user.ts"],"version":"5.6.3"}

0 comments on commit e80b560

Please sign in to comment.