Skip to content

Commit 1481a2b

Browse files
authored
Merge pull request #43 from Gavzzz/master
Nice to Have: Question History
2 parents 0847b49 + 6982655 commit 1481a2b

File tree

13 files changed

+397
-12
lines changed

13 files changed

+397
-12
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@
2222
npm-debug.log*
2323
yarn-debug.log*
2424
yarn-error.log*
25+
frontend/package-lock.json

frontend/package-lock.json

Lines changed: 18 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/src/api/user.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { AxiosResponse } from "axios";
2+
import { SolvedQuestion } from "../models/SolvedQuestion.model"; // Import the SolvedQuestion model
3+
import { userServiceClient } from "./server";
4+
5+
/**
6+
* Fetch questions completed by the user.
7+
* @param userId - The ID of the user for whom you want to fetch completed questions.
8+
* @returns An array of completed questions.
9+
*/
10+
export async function fetchUserCompletedQuestions(
11+
userId: String
12+
): Promise<SolvedQuestion[]> {
13+
try {
14+
const response: AxiosResponse = await userServiceClient.get(
15+
`/api/users/${userId}/questions`
16+
);
17+
18+
const resData = response.data;
19+
const completedQuestions: SolvedQuestion[] = resData.map(
20+
(q: any) =>
21+
new SolvedQuestion(
22+
q._id,
23+
q.id,
24+
q.title,
25+
q.description,
26+
q.topics,
27+
q.difficulty,
28+
false, // Default value for solved
29+
undefined // Default value for solvedDate
30+
)
31+
);
32+
33+
return completedQuestions;
34+
} catch (error) {
35+
// Handle errors appropriately, e.g., log the error, return an empty array, or throw an error.
36+
console.error(error);
37+
throw error;
38+
}
39+
}
40+
41+
/**
42+
* Add a new question to the user's completed questions.
43+
* @param userId - The ID of the user to whom you want to add the question.
44+
* @param questionId - The ID of the question you want to add.
45+
* @param complexity - The complexity of the question.
46+
* @param category - The category of the question.
47+
* @returns The added question.
48+
*/
49+
export async function addUserQuestion(
50+
userId: number,
51+
questionId: string,
52+
complexity: number,
53+
category: string[]
54+
): Promise<SolvedQuestion> {
55+
try {
56+
const response: AxiosResponse = await userServiceClient.post(
57+
`/api/users/${userId}/addquestions`,
58+
{
59+
userId,
60+
questionId,
61+
complexity,
62+
category,
63+
}
64+
);
65+
66+
const resData = response.data;
67+
const addedQuestion: SolvedQuestion = new SolvedQuestion(
68+
resData._id,
69+
resData.id,
70+
resData.title,
71+
resData.description,
72+
resData.topics,
73+
resData.difficulty,
74+
false, // Default value for solved
75+
undefined // Default value for solvedDate
76+
);
77+
78+
return addedQuestion;
79+
} catch (error) {
80+
// Handle errors appropriately, e.g., log the error or throw it to be handled by the caller.
81+
console.error(error);
82+
throw error;
83+
}
84+
}
85+
86+
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import React, { useEffect, useState } from "react";
2+
import {
3+
Table,
4+
Thead,
5+
Tbody,
6+
Tr,
7+
Th,
8+
Td,
9+
TableCaption,
10+
Center,
11+
TableContainer,
12+
} from "@chakra-ui/react";
13+
import { Paginator } from "../Paginator/Paginator.component";
14+
import { SolvedQuestion } from "../../models/SolvedQuestion.model";
15+
import { fetchUserCompletedQuestions } from "../../api/user"; // Import your API function
16+
17+
export type TableProp = {
18+
userId: String;
19+
pageSize?: number;
20+
};
21+
22+
export const SolvedTable = (props: TableProp) => {
23+
const { userId, pageSize = 10 } = props;
24+
const [currentPage, setCurrentPage] = useState(1);
25+
const [solvedQuestions, setSolvedQuestions] = useState<SolvedQuestion[]>([]);
26+
27+
const onPageChange = (page: React.SetStateAction<number>) => {
28+
setCurrentPage(page);
29+
};
30+
31+
useEffect(() => {
32+
const loadSolvedQuestions = async () => {
33+
if (userId) {
34+
try {
35+
const data = await fetchUserCompletedQuestions(userId);
36+
setSolvedQuestions(data);
37+
} catch (error) {
38+
console.error("Failed to fetch solved questions:", error);
39+
}
40+
}
41+
};
42+
43+
loadSolvedQuestions();
44+
}, [userId]);
45+
46+
const getQuestionsForPage = () => {
47+
const startIndex = (currentPage - 1) * pageSize;
48+
return solvedQuestions.slice(startIndex, startIndex + pageSize);
49+
};
50+
51+
return (
52+
<TableContainer width="100%">
53+
<Table
54+
variant="striped"
55+
backgroundColor={"white"}
56+
size="md"
57+
boxShadow="md"
58+
transition="box-shadow 0.2s"
59+
_hover={{
60+
boxShadow: "xl",
61+
}}
62+
width="60%"
63+
sx={{ tableLayout: "fixed" }}
64+
>
65+
<TableCaption>
66+
<Center>
67+
<Paginator
68+
adjacent={1}
69+
page={currentPage}
70+
totalPages={Math.ceil(solvedQuestions.length / pageSize)}
71+
onPageChange={onPageChange}
72+
/>
73+
</Center>
74+
</TableCaption>
75+
<Thead>
76+
<Tr>
77+
<Th colSpan={4}>Completed Questions</Th>
78+
</Tr>
79+
<Tr>
80+
<Th>Question</Th>
81+
<Th>Topics</Th>
82+
<Th isNumeric>Difficulty</Th>
83+
<Th isNumeric>Solved Date</Th>
84+
</Tr>
85+
</Thead>
86+
<Tbody>
87+
{getQuestionsForPage().map((question) => (
88+
<Tr key={question._id}>
89+
<Td>{question.title}</Td>
90+
<Td>{question.topics.join(", ")}</Td>
91+
<Td isNumeric>{question.difficulty}</Td>
92+
<Td isNumeric>
93+
{question.solvedDate
94+
? new Date(question.solvedDate).toLocaleDateString()
95+
: "Not Available"}
96+
</Td>
97+
</Tr>
98+
))}
99+
</Tbody>
100+
</Table>
101+
</TableContainer>
102+
);
103+
};

frontend/src/data/sampleqn.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,7 @@ export const getUnusedId = async () => {
6565
) + 1
6666
);
6767
};
68+
69+
70+
71+

frontend/src/models/Question.model.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ export class Question {
77
id: number;
88
_id: string;
99

10+
1011
constructor(
1112
_id: string,
1213
id: number,
1314
title: string,
1415
descMd: string,
1516
categories: string[],
16-
difficulty: number
17+
difficulty: number,
1718
) {
1819
this.title = title;
1920
this.descMd = descMd;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Question, QnFilter } from "./Question.model";
2+
3+
export class SolvedQuestion extends Question {
4+
solved: boolean;
5+
solvedDate: Date | undefined;
6+
7+
constructor(
8+
_id: string,
9+
id: number,
10+
title: string,
11+
descMd: string,
12+
categories: string[],
13+
difficulty: number,
14+
solved: boolean = false,
15+
solvedDate?: Date
16+
) {
17+
super(_id, id, title, descMd, categories, difficulty);
18+
this.solved = solved;
19+
this.solvedDate = solvedDate;
20+
}
21+
}
22+
23+
// Define a filter for SolvedQuestions, similar to QnFilter
24+
export type SolvedQnFilter = QnFilter & {
25+
solved?: boolean;
26+
solvedDate?: Date; // Change the type to Date
27+
};
Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
1-
import React from "react";
2-
import "./HomePage.css";
1+
import React, { useEffect, useState } from "react";
32
import "bootstrap/dist/css/bootstrap.min.css";
3+
import { useNavigate } from "react-router-dom";
4+
import { QuestionEditor } from "../../components/QuestionEditor/QuestionEditor.component";
5+
import { SolvedTable } from "../../components/SolvedTable/SolvedTable.component";
6+
import { selectUser } from "../../reducers/authSlice";
7+
import { useSelector } from "react-redux";
48

59
function HomePage() {
6-
return <></>;
10+
// Use the useSelector hook to access the user from the Redux store
11+
const user = useSelector(selectUser);
12+
13+
if (user === null) {
14+
return (
15+
<div>
16+
<QuestionEditor />
17+
</div>
18+
);
19+
}
20+
21+
// At this point, you're sure that user is not null
22+
return (
23+
<div>
24+
<QuestionEditor />
25+
<SolvedTable userId={user.id} pageSize={4} />
26+
</div>
27+
);
728
}
829

930
export default HomePage;

package-lock.json

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"dependencies": {
3+
"cookie-parser": "^1.4.6"
4+
}
5+
}

0 commit comments

Comments
 (0)