Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
74 changes: 74 additions & 0 deletions frontend/components/questions/question-filter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"use client";

import React from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "../ui/select";

interface QuestionFilterProps {
category: string;
onCategoryChange: (search: string) => void;
complexity: string;
onComplexityChange: (complexity: string) => void;
onReset: () => void;
}

const QuestionFilter: React.FC<QuestionFilterProps> = ({
category,
onCategoryChange,
complexity,
onComplexityChange,
onReset,
}) => {
return (
<Card className="mb-6">
<CardHeader>
<CardTitle>Filter Questions</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<Label htmlFor="category">Category</Label>
<Input
id="category"
value={category}
onChange={(e) => onCategoryChange(e.target.value)}
placeholder="Enter category"
className="mt-1"
/>
</div>
<div>
<Label htmlFor="complexity">Complexity</Label>
<Select
value={complexity}
onValueChange={(value) => onComplexityChange(value)}
>
<SelectTrigger id="complexity" className="mt-1">
<SelectValue placeholder="Select complexity" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All</SelectItem>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May can use a complexity schema, and then map over these fields?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using zod enum?

<SelectItem value="easy">Easy</SelectItem>
<SelectItem value="medium">Medium</SelectItem>
<SelectItem value="hard">Hard</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<Button onClick={onReset} variant="outline" className="mt-4">
Reset Filters
</Button>
</CardContent>
</Card>
);
};

export default QuestionFilter;
54 changes: 50 additions & 4 deletions frontend/components/questions/questions-listing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { useEffect, useState } from "react";
import useSWR from "swr";
import { Question, QuestionArraySchema } from "@/lib/schemas/question-schema";
import LoadingScreen from "@/components/common/loading-screen";
import { useRouter } from "next/navigation";
import { useRouter, useSearchParams } from "next/navigation";
import QuestionFilter from "./question-filter";

const fetcher = async (url: string): Promise<Question[]> => {
const token = localStorage.getItem("jwtToken");
Expand All @@ -33,9 +34,17 @@ const fetcher = async (url: string): Promise<Question[]> => {
export default function QuestionListing() {
const auth = useAuth();
const router = useRouter();
const searchParams = useSearchParams();
const [category, setCategory] = useState(searchParams.get("category") || "");
const [complexity, setComplexity] = useState(
searchParams.get("complexity") || ""
);
const { data, isLoading } = useSWR(
"http://localhost:8000/questions",
fetcher
`http://localhost:8000/questions?category=${encodeURIComponent(category)}&complexity=${encodeURIComponent(complexity)}`,
fetcher,
{
keepPreviousData: true,
}
);

const [questions, setQuestions] = useState<Question[]>([]);
Expand All @@ -44,17 +53,54 @@ export default function QuestionListing() {
setQuestions(data ?? []);
}, [data]);

useEffect(() => {
const params = new URLSearchParams(searchParams);
if (category) {
params.set("category", category);
} else {
params.delete("category");
}
if (complexity) {
params.set("complexity", complexity);
} else {
params.delete("complexity");
}
router.push(`?${params.toString()}`);
}, [category, complexity, router, searchParams]);

const handleView = (question: Question) => {
router.push(`/app/questions/${question.id}`);
};

if (isLoading) {
const handleSearchChange = (newSearch: string) => {
setCategory(newSearch);
};
const handleComplexityChange = (newComplexity: string) => {
if (newComplexity === "all") {
newComplexity = "";
}
setComplexity(newComplexity);
};

const handleReset = () => {
setCategory("");
router.push("");
};

if (isLoading && !data) {
return <LoadingScreen />;
}

return (
<div className="container mx-auto p-4">
<h1 className="text-2xl font-bold mb-4">Question Listing</h1>
<QuestionFilter
category={category}
onCategoryChange={handleSearchChange}
complexity={complexity}
onComplexityChange={handleComplexityChange}
onReset={handleReset}
/>
<QuestionTable
data={questions}
isAdmin={auth?.user?.isAdmin ?? false}
Expand Down
10 changes: 8 additions & 2 deletions question-service/app/crud/questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,14 @@ async def create_question(question: CreateQuestionModel):
new_question = await question_collection.insert_one(question.model_dump())
return await question_collection.find_one({"_id": new_question.inserted_id})

async def get_all_questions() -> QuestionCollection:
questions = await question_collection.find().to_list(1000)
async def get_all_questions(category: str, complexity: str) -> QuestionCollection:
query = {}
if category:
query["category"] = {"$regex": category, "$options": "i"}
if complexity:
query["complexity"] = complexity
questions = await question_collection.find(query).to_list(1000)

return QuestionCollection(questions=questions)

async def get_question_by_id(question_id: str) -> QuestionModel:
Expand Down
4 changes: 2 additions & 2 deletions question-service/app/routers/questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ async def create(question: CreateQuestionModel):
return existing_question

@router.get("/", response_description="Get all questions", response_model=QuestionCollection)
async def get_all():
return await get_all_questions()
async def get_all(category: str = None, complexity: str = None):
return await get_all_questions(category, complexity)

@router.get("/{question_id}", response_description="Get question with specified id", response_model=QuestionModel)
async def get_question(question_id: str):
Expand Down