Skip to content

Commit 2908bc0

Browse files
committed
Merge remote-tracking branch 'origin/qn-refactor' into enhance-indiv-qn
2 parents 4f79e77 + 710229b commit 2908bc0

File tree

14 files changed

+268
-146
lines changed

14 files changed

+268
-146
lines changed

peerprep/api/gateway.ts

Lines changed: 91 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,125 @@
11
import { Question, StatusBody, QuestionFullBody } from "./structs";
22

33
const questions: { [key: string]: Question } = {
4-
"0" : {
5-
"id": 0,
6-
"difficulty": 2,
7-
"title": "Two Sum",
8-
"description": "Given an array of integers, return indices of the two numbers such that they add up to a specific target.",
9-
"categories": ["Hash Table", "Array"],
10-
"test_cases": {
11-
"[2, 7, 11, 15], 9" : "[0, 1]",
12-
"[3, 2, 4], 6" : "[1, 2]",
13-
"[3, 3], 6" : "[0, 1]"
14-
}
4+
"0": {
5+
id: 0,
6+
difficulty: 2,
7+
title: "Two Sum",
8+
description:
9+
"Given an array of integers, return indices of the two numbers such that they add up to a specific target.",
10+
categories: ["Hash Table", "Array"],
11+
test_cases: {
12+
"[2, 7, 11, 15], 9": "[0, 1]",
13+
"[3, 2, 4], 6": "[1, 2]",
14+
"[3, 3], 6": "[0, 1]",
15+
},
16+
},
17+
"1": {
18+
id: 1,
19+
difficulty: 1,
20+
title: "Reverse Integer",
21+
description: "Given a 32-bit signed integer, reverse digits of an integer.",
22+
categories: ["Math"],
23+
test_cases: {
24+
"123": "321",
25+
"1": "1",
26+
"22": "22",
27+
},
1528
},
16-
"1" : {
17-
"id": 1,
18-
"difficulty": 1,
19-
"title": "Reverse Integer",
20-
"description": "Given a 32-bit signed integer, reverse digits of an integer.",
21-
"categories": ["Math"],
22-
"test_cases": {
23-
"123" : "321",
24-
"1" : "1",
25-
"22" : "22"
26-
}
27-
}
2829
};
2930

30-
export async function fetchQuestion(questionId: string): Promise<Question|StatusBody> {
31+
export async function fetchQuestion(
32+
questionId: string
33+
): Promise<Question | StatusBody> {
3134
// remove this when services are up
3235
if (process.env.DEV_ENV === "dev") {
3336
return questions[questionId] === undefined
34-
? {error: "Question not found", status: 404}
35-
: questions[questionId];
37+
? { error: "Question not found", status: 404 }
38+
: questions[questionId];
3639
}
3740
try {
38-
const response = await fetch(`${process.env.NEXT_PUBLIC_QUESTION_SERVICE}/questions/solve/${questionId}`);
41+
const response = await fetch(
42+
`${process.env.NEXT_PUBLIC_QUESTION_SERVICE}/questions/solve/${questionId}`
43+
);
3944
if (!response.ok) {
4045
return {
4146
error: await response.text(),
42-
status: response.status
47+
status: response.status,
4348
};
4449
}
45-
return await response.json() as Question;
50+
return (await response.json()) as Question;
4651
} catch (err: any) {
47-
return { error: err.message, status: 400};
52+
return { error: err.message, status: 400 };
4853
}
4954
}
5055

5156
export async function addQuestion(body: QuestionFullBody): Promise<StatusBody> {
5257
try {
5358
const response = await fetch(
54-
`${process.env.NEXT_PUBLIC_QUESTION_SERVICE}/questions`,
55-
{
56-
method: "POST",
57-
body: JSON.stringify(body).replace(/(\"difficulty\":)\"([1-3])\"/, `$1$2`),
58-
headers: {
59-
"Content-type": "application/json; charset=UTF-8"
60-
}
61-
}
59+
`${process.env.NEXT_PUBLIC_QUESTION_SERVICE}/questions`,
60+
{
61+
method: "POST",
62+
body: JSON.stringify(body).replace(
63+
/(\"difficulty\":)\"([1-3])\"/,
64+
`$1$2`
65+
),
66+
headers: {
67+
"Content-type": "application/json; charset=UTF-8",
68+
},
69+
}
6270
);
6371
if (response.ok) {
6472
return {
65-
status: response.status
73+
status: response.status,
6674
};
6775
}
6876
return {
6977
error: (await response.json())["Error adding question: "],
70-
status: response.status
78+
status: response.status,
7179
};
7280
} catch (err: any) {
73-
return { error: err.message, status: 0};
81+
return { error: err.message, status: 0 };
82+
}
83+
}
84+
85+
export async function deleteQuestion(question: Question): Promise<StatusBody> {
86+
try {
87+
const response = await fetch(
88+
`${process.env.NEXT_PUBLIC_QUESTION_SERVICE}/questions/delete/${question.id}`,
89+
{
90+
method: "DELETE",
91+
headers: {
92+
"Content-type": "application/json; charset=UTF-8",
93+
},
94+
}
95+
);
96+
if (response.ok) {
97+
return {
98+
status: response.status,
99+
};
100+
}
101+
return {
102+
error: (await response.json())["Error deleting question: "],
103+
status: response.status,
104+
};
105+
} catch (err: any) {
106+
return { error: err.message, status: 0 };
107+
}
108+
}
109+
110+
export async function getAllQuestions(): Promise<Question[] | StatusBody> {
111+
try {
112+
const response = await fetch(
113+
`${process.env.NEXT_PUBLIC_QUESTION_SERVICE}/questions`
114+
);
115+
if (!response.ok) {
116+
return {
117+
error: await response.text(),
118+
status: response.status,
119+
};
120+
}
121+
return (await response.json()) as Question[];
122+
} catch (err: any) {
123+
return { error: err.message, status: 400 };
74124
}
75125
}

peerprep/api/structs.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
export enum Difficulty {
2+
All = 0,
23
Easy = 1,
34
Medium,
4-
Hard
5+
Hard,
56
}
67

78
export interface QuestionBody {
@@ -28,6 +29,8 @@ export interface StatusBody {
2829
error?: string;
2930
}
3031

31-
export function isError(obj: Question | StatusBody): obj is StatusBody {
32+
export function isError(
33+
obj: Question[] | Question | StatusBody
34+
): obj is StatusBody {
3235
return (obj as StatusBody).status !== undefined;
3336
}

peerprep/app/globals.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ body,
1313
p,
1414
h1,
1515
h2 {
16-
@apply text-gray-100;
16+
@apply text-text-2;
1717
}

peerprep/app/questions/new/page.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ import RadioButtonGroup from '@/components/shared/form/RadioButtonGroup';
88
import FormTextAreaInput from '@/components/shared/form/FormTextAreaInput';
99
import { useRouter } from 'next/navigation';
1010

11-
type Props = {}
11+
type Props = {};
1212

1313
interface Mapping {
14-
key: string,
15-
value: string
14+
key: string;
15+
value: string;
1616
}
1717

1818
function NewQuestion({}: Props) {
@@ -78,17 +78,19 @@ function NewQuestion({}: Props) {
7878
const values = [...testCases];
7979
values.splice(idx, 1);
8080
setTestCases(values);
81-
}
82-
81+
};
82+
8383
const handleSubmission = async (e: FormEvent<HTMLFormElement>) => {
8484
e.preventDefault();
8585
setLoading(true);
8686
const question: QuestionFullBody = {
8787
...formData,
88-
test_cases: testCases.map((elem: Mapping) => ({
89-
[elem.key]: elem.value
90-
})).reduce((res, item) => ({...res, ...item}), {})
91-
}
88+
test_cases: testCases
89+
.map((elem: Mapping) => ({
90+
[elem.key]: elem.value,
91+
}))
92+
.reduce((res, item) => ({ ...res, ...item }), {}),
93+
};
9294
const status = await addQuestion(question);
9395
if (status.error) {
9496
console.log("Failed to add question.");
@@ -149,7 +151,7 @@ function NewQuestion({}: Props) {
149151
}>Submit</button>
150152
</form>
151153
</div>
152-
)
154+
);
153155
}
154156

155-
export default NewQuestion;
157+
export default NewQuestion;
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1+
"use client";
12
import React from "react";
3+
import { useRouter } from "next/navigation";
4+
import PeerprepButton from "../shared/PeerprepButton";
25

36
const Matchmaking = () => {
4-
return <div>Matchmaking</div>;
7+
const router = useRouter();
8+
return (
9+
<div className="p-4">
10+
<PeerprepButton onClick={() => router.push(`questions/new`)}>
11+
Add Question
12+
</PeerprepButton>
13+
</div>
14+
);
515
};
616

717
export default Matchmaking;

peerprep/components/questionpage/QuestionCard.tsx

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,80 @@
1+
"use client";
2+
import { deleteQuestion } from "@/api/gateway";
13
import React from "react";
2-
import { Question, difficulties } from "../shared/Question";
4+
import { Question, Difficulty } from "@/api/structs";
35
import PeerprepButton from "../shared/PeerprepButton";
6+
import { useRouter } from "next/navigation";
7+
import styles from "@/style/questionCard.module.css";
8+
import QuestionList from "./QuestionList";
49

510
type QuestionCardProps = {
611
question: Question;
712
};
813

914
const QuestionCard: React.FC<QuestionCardProps> = ({ question }) => {
10-
const getDifficultyClass = (difficulty: string) => {
15+
const router = useRouter();
16+
const handleDelete = async () => {
17+
if (
18+
confirm(
19+
`Are you sure you want to delete ${question.title}? (ID: ${question.id}) `
20+
)
21+
) {
22+
const status = await deleteQuestion(question);
23+
if (status.error) {
24+
console.log("Failed to delete question.");
25+
console.log(`Code ${status.status}: ${status.error}`);
26+
return;
27+
}
28+
console.log(`Successfully deleted the question.`);
29+
} else {
30+
console.log("Deletion cancelled.");
31+
}
32+
};
33+
34+
const getDifficultyColor = (difficulty: Difficulty) => {
1135
switch (difficulty) {
12-
case "easy":
13-
return "text-green-500"; // Green for easy
14-
case "medium":
15-
return "text-yellow-500"; // Yellow for medium
16-
case "hard":
17-
return "text-red-500"; // Red for hard
36+
case Difficulty.Easy:
37+
return "text-difficulty-easy"; // Green for easy
38+
case Difficulty.Medium:
39+
return "text-difficulty-med"; // Yellow for medium
40+
case Difficulty.Hard:
41+
return "text-difficulty-hard"; // Red for hard
1842
default:
19-
return "text-gray-100"; // Default color
43+
return "text-secondary-text"; // Default color
2044
}
2145
};
2246

2347
return (
24-
<div className="flex items-center w-full h-24 p-4 bg-gray-700 shadow-md rounded-lg mb-4">
25-
<div className="flex-none w-1/3 overflow-hidden">
26-
<h2 className="text-lg font-bold text-nowrap">{question.title}</h2>
27-
<p className="text-sm">
48+
<div className={styles.container}>
49+
<div className="flex-none w-full sm:w-1/3">
50+
<h2 className={styles.title}>{question.title}</h2>
51+
<p className={styles.bodytext}>
2852
Difficulty:{" "}
2953
<span
30-
className={`capitalize ${getDifficultyClass(
31-
difficulties[question.difficulty]
54+
className={`capitalize font-bold ${getDifficultyColor(
55+
question.difficulty
3256
)}`}
3357
>
34-
{difficulties[question.difficulty]}
58+
{Difficulty[question.difficulty]}
3559
</span>
3660
</p>
37-
<p className="text-sm">
38-
Categories: <span>{question.categories.join(", ")}</span>
61+
<p className={styles.bodytext}>
62+
Categories:{" "}
63+
<span>
64+
{question.categories ? question.categories.join(", ") : "None"}
65+
</span>
3966
</p>
4067
</div>
4168

42-
<div className="flex-none w-1/2 max-h-16 overflow-hidden">
43-
<p className="text-sm text-wrap truncate text-right">
44-
{question.description}
45-
</p>
69+
<div className="flex-none w-full sm:w-1/2 max-h-16">
70+
<p className={styles.bodytext}>{question.description}</p>
4671
</div>
4772

48-
<div className="ml-10 mr-10">
49-
{/* display question page */}
50-
<PeerprepButton onClick={() => console.log("Expand button clicked!")}>
51-
View Question
73+
<div className={styles.buttonContainer}>
74+
<PeerprepButton onClick={() => router.push(`questions/${question.id}`)}>
75+
View
5276
</PeerprepButton>
77+
<PeerprepButton onClick={handleDelete}>Delete</PeerprepButton>
5378
</div>
5479
</div>
5580
);

0 commit comments

Comments
 (0)