Skip to content

Commit 9a3494b

Browse files
committed
PEER-219: Add question attempt status to main view
Signed-off-by: SeeuSim <[email protected]>
1 parent 43f9d89 commit 9a3494b

File tree

6 files changed

+61
-45
lines changed

6 files changed

+61
-45
lines changed

backend/question/src/controller/question-controller.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ import type {
2121
} from '@/services/post/types';
2222

2323
export const getQuestions = async (req: Request, res: Response): Promise<Response> => {
24-
const { questionName, difficulty, topic, pageNum, recordsPerPage } = req.query;
24+
const { questionName, difficulty, topic, pageNum, recordsPerPage, userId } = req.query;
2525
const payload: IGetQuestionsPayload = {
2626
questionName: questionName as string,
2727
difficulty: difficulty as string,
2828
topic: topic as Array<string>,
2929
pageNum: parseInt(pageNum as string) || 0,
3030
recordsPerPage: parseInt(recordsPerPage as string) || 20,
31+
userId: userId as string,
3132
};
3233

3334
try {

backend/question/src/services/get/index.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { and, arrayOverlaps, eq, ilike, sql } from 'drizzle-orm';
1+
import { and, arrayOverlaps, eq, getTableColumns, ilike, or, sql } from 'drizzle-orm';
22
import { StatusCodes } from 'http-status-codes';
33

44
import { db } from '@/lib/db/index';
5-
import { questions } from '@/lib/db/schema';
5+
import { questionAttempts, questions } from '@/lib/db/schema';
66

77
import type {
88
IGetDifficultiesResponse,
@@ -16,7 +16,7 @@ import type {
1616
export const getQuestionsService = async (
1717
payload: IGetQuestionsPayload
1818
): Promise<IGetQuestionsResponse> => {
19-
const { questionName, difficulty, topic, pageNum = 0, recordsPerPage = 20 } = payload;
19+
const { questionName, difficulty, topic, pageNum = 0, recordsPerPage = 20, userId } = payload;
2020
const offset = pageNum * recordsPerPage;
2121

2222
const whereClause = [];
@@ -34,9 +34,20 @@ export const getQuestionsService = async (
3434
}
3535

3636
const query = db
37-
.select()
37+
.select({
38+
...getTableColumns(questions),
39+
attempted: sql`COALESCE(COUNT(${questionAttempts.attemptId}), 0)`.as('attempted'),
40+
})
3841
.from(questions)
42+
.leftJoin(
43+
questionAttempts,
44+
and(
45+
eq(questionAttempts.questionId, questions.id),
46+
or(eq(questionAttempts.userId1, userId), eq(questionAttempts.userId2, userId))
47+
)
48+
)
3949
.where(and(...whereClause))
50+
.groupBy(questions.id)
4051
.limit(recordsPerPage)
4152
.offset(offset)
4253
.orderBy(questions.id);
@@ -58,6 +69,7 @@ export const getQuestionsService = async (
5869
title: q.title,
5970
difficulty: q.difficulty,
6071
topic: q.topic,
72+
attempted: (q.attempted as number) > 0,
6173
})),
6274
totalQuestions: totalCount,
6375
},

backend/question/src/services/get/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { IServiceResponse } from '@/types';
55
//=============================================================================
66
export type IGetQuestionsPayload = {
77
// Filters
8+
userId: string;
89
questionName?: string;
910
difficulty?: string;
1011
topic?: Array<string>;

frontend/src/lib/router.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ import { Login } from '@/routes/login';
1010
import { loader as topicsLoader } from '@/routes/match/logic';
1111
import { Match } from '@/routes/match/main';
1212
import { loader as questionDetailsLoader, QuestionDetailsPage } from '@/routes/questions/details';
13-
import { loader as questionsLoader, Questions } from '@/routes/questions/main';
13+
import {
14+
// loader as questionsLoader,
15+
Questions,
16+
} from '@/routes/questions/main';
1417
import { SignUp } from '@/routes/signup';
1518

1619
import { queryClient } from './query-client';
@@ -33,7 +36,7 @@ export const router = createBrowserRouter([
3336
},
3437
{
3538
path: ROUTES.QUESTIONS,
36-
loader: questionsLoader(queryClient),
39+
// loader: questionsLoader(queryClient),
3740
element: <Questions />,
3841
},
3942
{

frontend/src/routes/questions/main.tsx

Lines changed: 32 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,60 @@
1-
import { type QueryClient, queryOptions, useInfiniteQuery } from '@tanstack/react-query';
2-
import { Suspense, useEffect, useMemo } from 'react';
3-
import { Await, defer, type LoaderFunctionArgs, useLoaderData } from 'react-router-dom';
1+
import {
2+
// type QueryClient, queryOptions,
3+
useInfiniteQuery,
4+
} from '@tanstack/react-query';
5+
import {
6+
// Suspense,
7+
useEffect,
8+
useMemo,
9+
} from 'react';
410

11+
// import { Await, defer, type LoaderFunctionArgs, useLoaderData } from 'react-router-dom';
512
import { WithNavBanner } from '@/components/blocks/authed';
6-
import { Loading } from '@/components/blocks/loading';
13+
// import { Loading } from '@/components/blocks/loading';
714
import { ScrollArea } from '@/components/ui/scroll-area';
815
import { usePageTitle } from '@/lib/hooks';
916
import { useCrumbs } from '@/lib/hooks/use-crumbs';
1017
import { ROUTES } from '@/lib/routes';
1118
import { fetchQuestions, ROWS_PER_PAGE } from '@/services/question-service';
19+
import { useAuthedRoute } from '@/stores/auth-store';
1220
import type { IGetQuestionsResponse } from '@/types/question-types';
1321

1422
import { QuestionTable } from './question-table';
1523
import { columns } from './table-columns';
1624

17-
const getListQuestionsQueryConfig = (pageNumber?: number) =>
18-
queryOptions({
19-
queryKey: ['qn', 'list', pageNumber],
20-
queryFn: async ({ signal: _ }) => fetchQuestions(pageNumber),
21-
});
22-
23-
export const loader =
24-
(queryClient: QueryClient) =>
25-
async ({ params: _ }: LoaderFunctionArgs) => {
26-
return defer({
27-
initialPage: queryClient.ensureQueryData(getListQuestionsQueryConfig()),
28-
});
29-
};
30-
31-
type IQuestionListServiceAPIResponse = Awaited<ReturnType<typeof fetchQuestions>>;
32-
type IQuestionLoaderReturn = Awaited<ReturnType<ReturnType<typeof loader>>>['data'];
33-
type IQuestionLoaderData = { initialPage?: IQuestionListServiceAPIResponse };
25+
// type IQuestionListServiceAPIResponse = Awaited<ReturnType<typeof fetchQuestions>>;
26+
// type IQuestionLoaderReturn = Awaited<ReturnType<ReturnType<typeof loader>>>['data'];
27+
// type IQuestionLoaderData = { initialPage?: IQuestionListServiceAPIResponse };
28+
// const getListQuestionsQueryConfig = (pageNumber?: number) =>
29+
// queryOptions({
30+
// queryKey: ['qn', 'list', pageNumber],
31+
// queryFn: async ({ signal: _ }) => fetchQuestions(pageNumber),
32+
// });
33+
// export const loader =
34+
// (queryClient: QueryClient) =>
35+
// async ({ params: _ }: LoaderFunctionArgs) => {
36+
// return defer({
37+
// initialPage: queryClient.ensureQueryData(getListQuestionsQueryConfig()),
38+
// });
39+
// };
3440

3541
export function Questions() {
42+
const { userId } = useAuthedRoute();
3643
usePageTitle(ROUTES.QUESTIONS);
3744
const { crumbs } = useCrumbs();
3845

39-
const initialData = useLoaderData() as IQuestionLoaderReturn as IQuestionLoaderData;
40-
4146
const { data, fetchNextPage, hasNextPage, isError, isFetchingNextPage } = useInfiniteQuery<
4247
IGetQuestionsResponse,
4348
Error
4449
>({
45-
queryKey: ['questions'],
46-
queryFn: ({ pageParam }) => fetchQuestions(pageParam as number | undefined),
50+
queryKey: ['questions', userId],
51+
queryFn: ({ pageParam }) => fetchQuestions(userId, pageParam as number | undefined),
4752
initialPageParam: 0,
4853
getNextPageParam: (lastPage, pages) => {
4954
const nextPage = pages.length;
5055
const totalPages = Math.ceil(lastPage.totalQuestions / ROWS_PER_PAGE);
5156
return nextPage < totalPages ? nextPage : undefined;
5257
},
53-
initialData: {
54-
pages: initialData?.initialPage?.questions ? [initialData.initialPage] : [],
55-
pageParams: initialData?.initialPage?.questions ? [0] : [],
56-
},
5758
});
5859

5960
useEffect(() => {
@@ -65,22 +66,16 @@ export function Questions() {
6566
const questions = useMemo(() => {
6667
if (data) {
6768
return data.pages.flatMap((page) => page.questions);
68-
} else if (initialData?.initialPage?.questions) {
69-
return initialData.initialPage.questions;
7069
}
7170

7271
return [];
73-
}, [data, initialData]);
72+
}, [data]);
7473

7574
return (
7675
<WithNavBanner crumbs={crumbs}>
7776
<div className='flex w-full flex-1 overflow-hidden py-3'>
7877
<ScrollArea className='size-full px-6'>
79-
<Suspense fallback={<Loading />}>
80-
<Await resolve={initialData.initialPage}>
81-
<QuestionTable columns={columns} data={questions} isError={isError} />
82-
</Await>
83-
</Suspense>
78+
<QuestionTable columns={columns} data={questions} isError={isError} />
8479
</ScrollArea>
8580
</div>
8681
</WithNavBanner>

frontend/src/services/question-service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ export const getQuestionDetails = (questionId: number): Promise<IGetQuestionDeta
3131

3232
export const ROWS_PER_PAGE = 8;
3333

34-
export async function fetchQuestions(pageNum: number = 0): Promise<IGetQuestionsResponse> {
34+
export async function fetchQuestions(
35+
userId: string,
36+
pageNum: number = 0
37+
): Promise<IGetQuestionsResponse> {
3538
const params = new URLSearchParams({
39+
userId,
3640
pageNum: String(pageNum),
3741
recordsPerPage: String(ROWS_PER_PAGE),
3842
}).toString();

0 commit comments

Comments
 (0)