From 7d6b3715503515b9aa6d2c7ab83f978e1fb4513b Mon Sep 17 00:00:00 2001 From: wr1159 Date: Wed, 25 Sep 2024 00:57:52 +0800 Subject: [PATCH 1/4] Add question search filter to filter by question title --- .../components/questions/question-filter.tsx | 47 +++++++++++++++++++ .../questions/questions-listing.tsx | 40 ++++++++++++++-- question-service/app/crud/questions.py | 7 ++- question-service/app/routers/questions.py | 5 +- 4 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 frontend/components/questions/question-filter.tsx diff --git a/frontend/components/questions/question-filter.tsx b/frontend/components/questions/question-filter.tsx new file mode 100644 index 0000000000..549c88f6b2 --- /dev/null +++ b/frontend/components/questions/question-filter.tsx @@ -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 = ({ + search, + onSearchChange, + onReset, +}) => { + return ( + + + Filter Questions + + +
+
+ + onSearchChange(e.target.value)} + placeholder="Search questions" + className="mt-1" + autoFocus + /> +
+
+ +
+
+ ); +}; + +export default QuestionFilter; diff --git a/frontend/components/questions/questions-listing.tsx b/frontend/components/questions/questions-listing.tsx index df4c25cc52..d718473445 100644 --- a/frontend/components/questions/questions-listing.tsx +++ b/frontend/components/questions/questions-listing.tsx @@ -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 => { const token = localStorage.getItem("jwtToken"); @@ -33,9 +34,16 @@ const fetcher = async (url: string): Promise => { 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([]); @@ -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 ; } return (

Question Listing

+ 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: diff --git a/question-service/app/routers/questions.py b/question-service/app/routers/questions.py index 12f182df5c..3eebdf2c17 100644 --- a/question-service/app/routers/questions.py +++ b/question-service/app/routers/questions.py @@ -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): From 357711edf7960102e0d02b611360ef3d9dcdc598 Mon Sep 17 00:00:00 2001 From: wr1159 Date: Thu, 26 Sep 2024 23:13:34 +0800 Subject: [PATCH 2/4] Add suspense parent for question listing --- frontend/app/app/questions/page.tsx | 7 ++++++- frontend/components/questions/questions-listing.tsx | 7 ++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/frontend/app/app/questions/page.tsx b/frontend/app/app/questions/page.tsx index 4000002a69..66f5d5ade1 100644 --- a/frontend/app/app/questions/page.tsx +++ b/frontend/app/app/questions/page.tsx @@ -1,5 +1,10 @@ import QuestionListing from "@/components/questions/questions-listing"; +import { Suspense } from "react"; export default function QuestionListingPage() { - return ; + return ( + + + + ); } diff --git a/frontend/components/questions/questions-listing.tsx b/frontend/components/questions/questions-listing.tsx index d718473445..b9bdd8d74c 100644 --- a/frontend/components/questions/questions-listing.tsx +++ b/frontend/components/questions/questions-listing.tsx @@ -1,13 +1,14 @@ "use client"; import { useAuth } from "@/app/auth/auth-context"; -import QuestionTable from "@/components/questions/questions-table"; import { useEffect, useState } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; import useSWR from "swr"; import { Question, QuestionArraySchema } from "@/lib/schemas/question-schema"; + import LoadingScreen from "@/components/common/loading-screen"; -import { useRouter, useSearchParams } from "next/navigation"; -import QuestionFilter from "./question-filter"; +import QuestionTable from "@/components/questions/questions-table"; +import QuestionFilter from "@/components/questions/question-filter"; const fetcher = async (url: string): Promise => { const token = localStorage.getItem("jwtToken"); From 4a7b2b669eb9d75c4b469951e0e90821e14ce711 Mon Sep 17 00:00:00 2001 From: wr1159 Date: Fri, 27 Sep 2024 22:24:46 +0800 Subject: [PATCH 3/4] Fix build errors --- frontend/components/questions/questions-listing.tsx | 3 +-- question-service/app/routers/questions.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/components/questions/questions-listing.tsx b/frontend/components/questions/questions-listing.tsx index 5df252f87a..5c1bc4afa7 100644 --- a/frontend/components/questions/questions-listing.tsx +++ b/frontend/components/questions/questions-listing.tsx @@ -48,7 +48,6 @@ export default function QuestionListing() { searchParams.get("complexity") || "" ); const { toast } = useToast(); - const searchParams = useSearchParams(); const [search, setSearch] = useState(searchParams.get("search") || ""); @@ -239,7 +238,7 @@ export default function QuestionListing() { router.push(""); }; - if (isLoading && !data && !data) { + if (isLoading && !data) { return ; } diff --git a/question-service/app/routers/questions.py b/question-service/app/routers/questions.py index 06cb4c3566..eccdead390 100644 --- a/question-service/app/routers/questions.py +++ b/question-service/app/routers/questions.py @@ -28,7 +28,7 @@ async def create(question: CreateQuestionModel): @router.get("/", response_description="Get all questions", response_model=QuestionCollection) async def get_all(category: str = None, complexity: str = None, search: str = None): - return get_all_questions(category, complexity, search) + return await get_all_questions(category, complexity, search) @router.get("/{question_id}", response_description="Get question with specified id", response_model=QuestionModel) async def get_question(question_id: str): From 4595a9d9bb0c2af7b673051ec62358a8e7552671 Mon Sep 17 00:00:00 2001 From: wr1159 Date: Fri, 27 Sep 2024 22:29:34 +0800 Subject: [PATCH 4/4] Fix handle reset --- frontend/components/questions/questions-listing.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/components/questions/questions-listing.tsx b/frontend/components/questions/questions-listing.tsx index 5c1bc4afa7..2c23d89d01 100644 --- a/frontend/components/questions/questions-listing.tsx +++ b/frontend/components/questions/questions-listing.tsx @@ -222,6 +222,7 @@ export default function QuestionListing() { const handleCategoryChange = (newSearch: string) => { setCategory(newSearch); }; + const handleComplexityChange = (newComplexity: string) => { if (newComplexity === "all") { newComplexity = ""; @@ -235,6 +236,8 @@ export default function QuestionListing() { const handleReset = () => { setSearch(""); + setCategory(""); + setComplexity(""); router.push(""); };