Skip to content

Commit edb1a30

Browse files
authored
Merge pull request #5 from CS3219-AY2425S1/frontend
Merge Frontend into Main
2 parents c9386a8 + 5e3ac1d commit edb1a30

36 files changed

+6267
-0
lines changed

peerprep/.eslintrc.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "next/core-web-vitals"
3+
}

peerprep/.gitignore

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# environment file
4+
.env
5+
6+
# dependencies
7+
/node_modules
8+
/.pnp
9+
.pnp.js
10+
11+
# testing
12+
/coverage
13+
14+
# next.js
15+
/.next/
16+
/out/
17+
18+
# production
19+
/build
20+
21+
# misc
22+
.DS_Store
23+
*.pem
24+
25+
# debug
26+
npm-debug.log*
27+
yarn-debug.log*
28+
yarn-error.log*
29+
30+
# local env files
31+
.env*.local
32+
33+
# vercel
34+
.vercel
35+
36+
# typescript
37+
*.tsbuildinfo
38+
next-env.d.ts

peerprep/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2+
3+
## Getting Started
4+
5+
First, run the development server:
6+
7+
```bash
8+
npm run dev
9+
# or
10+
yarn dev
11+
# or
12+
pnpm dev
13+
```
14+
15+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
16+
17+
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
18+
19+
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
20+
21+
## Learn More
22+
23+
To learn more about Next.js, take a look at the following resources:
24+
25+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27+
28+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
29+
30+
## Deploy on Vercel
31+
32+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
33+
34+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

peerprep/api/gateway.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { Question, StatusBody, QuestionFullBody } from "./structs";
2+
3+
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+
"test_cases": {
10+
"[2, 7, 11, 15], 9" : "[0, 1]",
11+
"[3, 2, 4], 6" : "[1, 2]",
12+
"[3, 3], 6" : "[0, 1]"
13+
}
14+
},
15+
"1" : {
16+
"id": 1,
17+
"difficulty": 1,
18+
"title": "Reverse Integer",
19+
"description": "Given a 32-bit signed integer, reverse digits of an integer.",
20+
"test_cases": {
21+
"123" : "321",
22+
"1" : "1",
23+
"22" : "22"
24+
}
25+
}
26+
};
27+
28+
export async function fetchQuestion(questionId: string): Promise<Question|StatusBody> {
29+
// remove this when services are up
30+
if (process.env.DEV_ENV === "dev") {
31+
return questions[questionId] === undefined
32+
? {error: "Question not found", status: 404}
33+
: questions[questionId];
34+
}
35+
try {
36+
const response = await fetch(`${process.env.NEXT_PUBLIC_QUESTION_SERVICE}/questions/solve/${questionId}`);
37+
if (!response.ok) {
38+
return {
39+
...(await response.json()),
40+
status: response.status
41+
};
42+
}
43+
return await response.json() as Question;
44+
} catch (err: any) {
45+
return { error: err.message, status: 0};
46+
}
47+
}
48+
49+
export async function addQuestion(body: QuestionFullBody): Promise<StatusBody> {
50+
try {
51+
const response = await fetch(
52+
`${process.env.NEXT_PUBLIC_QUESTION_SERVICE}/questions`,
53+
{
54+
method: "POST",
55+
body: JSON.stringify(body).replace(/(\"difficulty\":)\"([1-3])\"/, `$1$2`),
56+
headers: {
57+
"Content-type": "application/json; charset=UTF-8"
58+
}
59+
}
60+
);
61+
if (response.ok) {
62+
return {
63+
status: response.status
64+
};
65+
}
66+
return {
67+
error: (await response.json())["Error adding question: "],
68+
status: response.status
69+
};
70+
} catch (err: any) {
71+
return { error: err.message, status: 0};
72+
}
73+
}

peerprep/api/structs.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
export enum Difficulty {
2+
Easy = 1,
3+
Medium,
4+
Hard
5+
}
6+
7+
export interface QuestionBody {
8+
difficulty: Difficulty;
9+
title: string;
10+
description: string;
11+
}
12+
13+
export interface TestCase {
14+
test_cases: {
15+
[key: string]: string;
16+
};
17+
}
18+
19+
export interface QuestionFullBody extends QuestionBody, TestCase {}
20+
21+
export interface Question extends QuestionFullBody {
22+
id: number;
23+
}
24+
25+
export interface StatusBody {
26+
status: number;
27+
error?: string;
28+
}
29+
30+
export function isError(obj: Question | StatusBody): obj is StatusBody {
31+
return (obj as StatusBody).error !== undefined;
32+
}

peerprep/app/globals.css

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;
4+
5+
html,
6+
body {
7+
@apply overflow-hidden;
8+
@apply bg-black;
9+
height: 100%;
10+
}
11+
12+
body,
13+
p,
14+
h1,
15+
h2 {
16+
@apply text-gray-100;
17+
}

peerprep/app/layout.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import TitleBar from "@/components/shared/TitleBar";
2+
import "./globals.css";
3+
import type { Metadata } from "next";
4+
import { Inter } from "next/font/google";
5+
import Container from "@/components/shared/Container";
6+
import styles from "@/style/layout.module.css";
7+
8+
const inter = Inter({ subsets: ["latin"] });
9+
10+
export const metadata: Metadata = {
11+
title: "Create Next App",
12+
description: "Generated by create next app",
13+
};
14+
15+
export default function RootLayout({
16+
children,
17+
}: {
18+
children: React.ReactNode;
19+
}) {
20+
return (
21+
<html lang="en">
22+
<body className={inter.className}>
23+
<TitleBar />
24+
<Container className={styles.main_container}>{children}</Container>
25+
</body>
26+
</html>
27+
);
28+
}

peerprep/app/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import Link from "next/link";
2+
3+
export default function Home() {
4+
return <Link href="/questions">Questions</Link>;
5+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { fetchQuestion } from '@/api/gateway';
2+
import { Question as QnType, StatusBody, isError } from "@/api/structs";
3+
import styles from '@/style/question.module.css';
4+
import ErrorBlock from '@/components/shared/ErrorBlock';
5+
import React from 'react'
6+
import QuestionBlock from './question';
7+
8+
type Props = {
9+
params: {
10+
question: string
11+
}
12+
}
13+
14+
async function Question({ params }: Props) {
15+
const question = await fetchQuestion(params.question);
16+
17+
return (
18+
<div className={styles.wrapper}>
19+
{
20+
isError(question)
21+
? <ErrorBlock err={question as StatusBody}/>
22+
: <QuestionBlock question={question as QnType}/>
23+
}
24+
</div>
25+
)
26+
}
27+
28+
export default Question;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { Question, StatusBody, Difficulty } from "@/api/structs";
2+
import styles from '@/style/question.module.css';
3+
4+
interface Props {
5+
question : Question;
6+
}
7+
8+
const difficultyColor = (diff: Difficulty) => {
9+
return (
10+
diff === Difficulty.Easy ? <p className={`${styles.title} ${styles.easy}`}>Easy</p>
11+
: diff === Difficulty.Medium ? <p className={`${styles.title} ${styles.med}`}>Med</p>
12+
: <p className={`${styles.title} ${styles.hard}`}>Hard</p>
13+
);
14+
}
15+
16+
function QuestionBlock({ question }: Props) {
17+
const keys = question.test_cases ? Object.keys(question.test_cases) : [];
18+
19+
const createRow = (key: string) => (
20+
<tr key={key}>
21+
<td className={`${styles.table} ${styles.cell}`}>{key}</td>
22+
<td className={`${styles.table} ${styles.cell}`}>{question.test_cases[key]}</td>
23+
</tr>
24+
);
25+
26+
return (
27+
<>
28+
<div className={styles.qn_container}>
29+
<div className={styles.title_wrapper}>
30+
<h1 className={styles.title}>Q{question.id}: {question.title}</h1>
31+
{difficultyColor(question.difficulty)}
32+
</div>
33+
<br/>
34+
<p>{question.description}</p>
35+
<br/>
36+
{question.test_cases && (
37+
<table className={styles.table}>
38+
<tbody>
39+
<tr>
40+
<th className={`${styles.table} ${styles.header} ${styles.input}`}>Input</th>
41+
<th className={`${styles.table} ${styles.header} ${styles.output}`}>Expected Output</th>
42+
</tr>
43+
{keys.map(createRow)}
44+
</tbody>
45+
</table>
46+
)}
47+
</div>
48+
<form className={styles.editor_container}>
49+
<textarea className={styles.code_editor}/>
50+
</form>
51+
</>
52+
);
53+
}
54+
55+
export default QuestionBlock;

0 commit comments

Comments
 (0)