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

interface QuestionFilterProps {
search: string;
onSearchChange: (search: string) => void;
onReset: () => void;
}

const QuestionFilter: React.FC<QuestionFilterProps> = ({
search,
onSearchChange,
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="search">Search</Label>
<Input
id="search"
value={search}
onChange={(e) => onSearchChange(e.target.value)}
placeholder="Search questions"
className="mt-1"
autoFocus
/>
</div>
</div>
<Button onClick={onReset} variant="outline" className="mt-4">
Reset Filters
</Button>
</CardContent>
</Card>
);
};

export default QuestionFilter;
40 changes: 36 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";

Choose a reason for hiding this comment

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

To use absolute import statement

Copy link
Author

Choose a reason for hiding this comment

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

Updated in 357711e


const fetcher = async (url: string): Promise<Question[]> => {
const token = localStorage.getItem("jwtToken");
Expand All @@ -33,9 +34,16 @@ const fetcher = async (url: string): Promise<Question[]> => {
export default function QuestionListing() {
const auth = useAuth();
const router = useRouter();
const searchParams = useSearchParams();

const [search, setSearch] = useState(searchParams.get("search") || "");

const { data, isLoading } = useSWR(
"http://localhost:8000/questions",
fetcher
`http://localhost:8000/questions?search=${encodeURIComponent(search)}`,
fetcher,
{
keepPreviousData: true,
}
);

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

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

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

if (isLoading) {
const handleSearchChange = (newSearch: string) => {
setSearch(newSearch);
};

const handleReset = () => {
setSearch("");
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
search={search}
onSearchChange={handleSearchChange}
onReset={handleReset}
/>
<QuestionTable
data={questions}
isAdmin={auth?.user?.isAdmin ?? false}
Expand Down
7 changes: 5 additions & 2 deletions question-service/app/crud/questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ 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(search: str = None) -> QuestionCollection:
query = {}
if search:
query = {"title": {"$regex": search, "$options": "i"}}
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
5 changes: 3 additions & 2 deletions question-service/app/routers/questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ 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(search: str = None):
questions = await get_all_questions(search)
return questions

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