Skip to content

Commit 883a3ad

Browse files
committed
Merge branch 'main' of https://github.com/CS3219-AY2425S1/cs3219-ay2425s1-project-g15 into jehou/disconnect
2 parents bc24db9 + 4571050 commit 883a3ad

File tree

8 files changed

+209
-107
lines changed

8 files changed

+209
-107
lines changed

frontend/src/app/(home)/components/code-snippet/CodeSnippetHighlight.tsx

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
"use client";
2+
13
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
24
import { darcula } from "react-syntax-highlighter/dist/esm/styles/prism";
5+
import { useEffect, useState, useRef } from "react";
36

47
const CodeSnippetHighlight = () => {
5-
const code = `class Solution:
8+
const code = `
9+
class Solution:
610
def twoSum(self, nums: List[int], target: int) -> List[int]:
711
idxDict = {}
812
for i, num in enumerate(nums):
@@ -12,9 +16,43 @@ const CodeSnippetHighlight = () => {
1216
diff = target - num
1317
if diff in idxDict and i != idxDict[diff]:
1418
return [i, idxDict[diff]]`;
19+
20+
const [displayedText, setDisplayedText] = useState(""); // Store the currently displayed text
21+
const currentCharIndexRef = useRef(0); // Track the current character index
22+
const typingInterval = 30; // Milliseconds between each character
23+
24+
useEffect(() => {
25+
const intervalId = setInterval(() => {
26+
if (currentCharIndexRef.current < code.length) {
27+
// Append the next character if it exists
28+
const nextChar = code[currentCharIndexRef.current];
29+
if (nextChar !== undefined) {
30+
setDisplayedText((prevText) => prevText + nextChar);
31+
currentCharIndexRef.current += 1;
32+
}
33+
} else {
34+
// Stop the interval once all characters are displayed
35+
clearInterval(intervalId);
36+
}
37+
}, typingInterval);
38+
39+
return () => clearInterval(intervalId); // Cleanup on unmount
40+
}, []);
41+
1542
return (
16-
<SyntaxHighlighter language="python" style={darcula}>
17-
{code}
43+
<SyntaxHighlighter
44+
language="python"
45+
style={darcula}
46+
showLineNumbers={true}
47+
wrapLines={true}
48+
lineProps={{ style: { wordBreak: "break-all", whiteSpace: "pre-wrap" } }}
49+
customStyle={{
50+
height: "50%",
51+
width: "100%",
52+
borderRadius: "10px",
53+
}}
54+
>
55+
{displayedText}
1856
</SyntaxHighlighter>
1957
);
2058
};
Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,61 @@
11
import CodeSnippet from "@/app/(home)/components/code-snippet/CodeSnippetHighlight";
22
import { Button } from "@/components/ui/button";
3-
import { Card, CardContent } from "@/components/ui/card";
43
import { useRouter } from "next/navigation";
54
import Navbar from "@/app/common/Navbar";
65
import { getAuthStatus } from "@/api/user";
76
import { AuthStatus } from "@/types/user";
7+
import { useEffect, useState } from "react";
88

99
const LandingPage = () => {
10-
const router = useRouter();
11-
const status = getAuthStatus();
10+
const router = useRouter();
11+
const [authStatus, setAuthStatus] = useState<AuthStatus>(
12+
AuthStatus.UNAUTHENTICATED
13+
);
1214

13-
return (
14-
<>
15-
<Navbar />
16-
<div className="flex items-center justify-center min-h-[90vh]">
17-
<div className="flex justify-between max-w-[90vw]">
18-
<div className="flex-1 pr-10 h-full">
19-
<h1 className="text-6xl font-extrabold text-white pb-8">
20-
Collaborative Coding, Competitive Results.
21-
</h1>
22-
<p className="font-normal text-white pb-8">
23-
Join PeerPrep to sharpen your skills through real-time problem-solving, and prepare to outshine in every interview.
24-
Created for CS3219 Software Engineering Principles AY24/25 by Group 15.
25-
</p>
26-
<div className="pt-8">
27-
<Button
28-
className="font-semibold w-full"
29-
onClick={() => status === AuthStatus.UNAUTHENTICATED ? router.push("/login") : router.push("/match")}
30-
>
31-
<span className="pl-2">Get Started</span>
32-
</Button>
33-
</div>
34-
</div>
35-
<div className="flex-1 h-full">
36-
<Card className="bg-primary-1000 border-none pt-2 pb-2 max-w-[90vw] h-full drop-shadow">
37-
<CardContent className="h-full">
38-
<CodeSnippet />
39-
</CardContent>
40-
</Card>
41-
</div>
42-
</div>
43-
</div>
44-
</>
45-
);
15+
useEffect(() => {
16+
setAuthStatus(getAuthStatus());
17+
}, []);
18+
19+
const handleRedirect = () => {
20+
if (authStatus === AuthStatus.UNAUTHENTICATED) {
21+
router.push("/login");
22+
} else {
23+
router.push("/dashboard");
24+
}
25+
};
26+
27+
return (
28+
<div className="flex flex-col min-h-full">
29+
<Navbar />
30+
<div className="grid grid-cols-2 gap-10 h-full items-center justify-center pl-10 pr-10">
31+
<div className="">
32+
<h1 className="text-6xl font-extrabold text-white pb-8">
33+
Collaborative Coding, <br></br> Competitive Results.
34+
</h1>
35+
<p className="font-normal text-white pb-8 text-lg">
36+
Join PeerPrep to sharpen your skills through real-time
37+
problem-solving, and prepare to outshine in every interview. Created
38+
for CS3219 Software Engineering Principles AY24/25 by Group 15.
39+
</p>
40+
<div className="pt-8">
41+
<Button
42+
className="font-semibold w-full"
43+
onClick={() => handleRedirect()}
44+
>
45+
<span className="pl-2">
46+
{authStatus === AuthStatus.UNAUTHENTICATED
47+
? "Get Started"
48+
: "View Dashboard"}
49+
</span>
50+
</Button>
51+
</div>
52+
</div>
53+
<div className="flex items-center justify-items-center h-full">
54+
<CodeSnippet />
55+
</div>
56+
</div>
57+
</div>
58+
);
4659
};
4760

48-
export default LandingPage;
61+
export default LandingPage;

frontend/src/app/(user)/user/me/page.tsx

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,20 @@ import { zodResolver } from "@hookform/resolvers/zod";
2626
import Swal from "sweetalert2";
2727
import { CgProfile } from "react-icons/cg";
2828
import MoonLoader from "react-spinners/MoonLoader";
29+
import { IoCloseCircle } from "react-icons/io5";
2930

3031
const formSchema = z.object({
3132
username: z
3233
.string()
3334
.min(5, "Username must be at least 5 characters")
3435
.max(20, "Username must be at most 20 characters")
3536
.optional(),
36-
profilePictureUrl: z.string().url("Invalid URL").optional(),
37+
profilePictureUrl: z
38+
.string()
39+
.optional()
40+
.refine((value) => !value || z.string().url().safeParse(value).success, {
41+
message: "Invalid URL",
42+
}),
3743
email: z.string().email("Invalid email").optional(),
3844
password: z
3945
.string()
@@ -73,8 +79,6 @@ const ProfilePage = () => {
7379
},
7480
});
7581

76-
const { isDirty } = form.formState;
77-
7882
useEffect(() => {
7983
setToken((_) => !!getToken());
8084
}, []);
@@ -87,6 +91,7 @@ const ProfilePage = () => {
8791
}, [form]);
8892

8993
const onSubmit = async (data: z.infer<typeof formSchema>) => {
94+
console.log(data);
9095
// remove unnecessary fields
9196
if (!data.password) delete data.password;
9297
if (data.password && data.password.length < 8) {
@@ -102,13 +107,15 @@ const ProfilePage = () => {
102107

103108
const triggerFileInput = () => {
104109
if (fileInputRef.current && !isLoading) {
110+
console.log("Click");
105111
fileInputRef.current.click();
106112
}
107113
};
108114

109115
const handleProfilePictureUpload = async (
110116
e: React.ChangeEvent<HTMLInputElement>
111117
) => {
118+
console.log("Uploading profile picture...");
112119
try {
113120
if (e.target.files) {
114121
const imageFile = e.target.files[0];
@@ -127,6 +134,7 @@ const ProfilePage = () => {
127134
} catch (error) {
128135
console.log(error);
129136
} finally {
137+
console.log("Done");
130138
setIsLoading(false);
131139
}
132140
};
@@ -167,13 +175,30 @@ const ProfilePage = () => {
167175
+
168176
</div>
169177
)}
178+
<IoCloseCircle
179+
className="absolute top-0 right-0 -mt-2 -mr-2 text-red-500 bg-white rounded-full cursor-pointer"
180+
size={24}
181+
onClick={(e) => {
182+
e.stopPropagation();
183+
form.setValue("profilePictureUrl", "");
184+
setUser({ ...user, profilePictureUrl: "" });
185+
}}
186+
/>
170187
</div>
171188
) : (
172-
<CgProfile
173-
size={150}
174-
className="hover:bg-primary-300 hover:cursor-pointer hover:rounded-full"
175-
onClick={triggerFileInput}
176-
/>
189+
<div>
190+
{isLoading ? (
191+
<div className="w-40 h-40 flex items-center justify-center rounded-full bg-primary-300">
192+
<MoonLoader />
193+
</div>
194+
) : (
195+
<CgProfile
196+
size={150}
197+
className="hover:bg-primary-300 hover:cursor-pointer hover:rounded-full"
198+
onClick={triggerFileInput}
199+
/>
200+
)}
201+
</div>
177202
)}
178203
<input
179204
type="file"

frontend/src/app/collaboration/components/question.tsx

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,11 @@ const Question = ({
152152
});
153153

154154
client.subscribe("/user/queue/language", (message) => {
155-
const messageReceived = message.body;
155+
const messageReceived: SingleChatLogApiResponse = JSON.parse(
156+
message.body
157+
);
156158
isLanguageChangeActive.current = false;
157-
setLanguage(messageReceived);
159+
setLanguage(messageReceived.message);
158160
Swal.fire({
159161
title: "Language changed by your collaborator!",
160162
icon: "success",
@@ -294,7 +296,7 @@ const Question = ({
294296
);
295297

296298
return (
297-
<div className="px-2 grid grid-rows-[20%_45%_35%] gap-2 grid-cols-1 h-full items-start">
299+
<div className="px-2 grid grid-rows-[20%_45%_35%] gap-2 grid-cols-1 h-full items-start max-h-[100vh]">
298300
<div className="mt-4 row-span-1 grid grid-rows-1 grid-cols-[75%_25%] w-full h-full">
299301
<div className="flex flex-col" ref={containerRef}>
300302
<h1
@@ -359,29 +361,31 @@ const Question = ({
359361
Exit Room
360362
</Button>
361363
</div>
362-
<span className="row-span-1 text-primary-300 max-h-[100%] h-full overflow-y-auto flex flex-col bg-primary-800 p-3 rounded-md">
363-
<span className="text-yellow-500 font-bold text-md">Question Description</span>
364+
<span className="row-span-1 text-primary-300 text-md max-h-[100%] h-full overflow-y-auto flex flex-col gap-2 bg-primary-800 p-3 rounded-md">
365+
<span className="text-yellow-500 font-bold text-md">
366+
Question Description
367+
</span>
364368
<span className="text-white py-2 text-xs">{question?.description}</span>
365-
<span className="text-yellow-500 font-bold">Examples</span>
369+
<span className="text-yellow-500 font-bold text-md">Examples</span>
366370
{question?.examples?.map((example, idx) => (
367371
<div key={idx}>
368-
<div className="font-bold underline">
372+
<div className="font-bold underline text-xs">
369373
Example {example.example_num}:
370374
</div>
371375
<div>
372-
<span className="font-bold">Expected Input: </span>
373-
<span className="text-primary-400 tracking-wide">
376+
<span className="font-bold text-xs">Expected Input: </span>
377+
<span className="text-primary-400 tracking-wide text-xs">
374378
{example.expected_input}
375379
</span>
376380
</div>
377381
<div>
378-
<span className="font-bold">Expected Output: </span>
379-
<span className="text-primary-400 tracking-wide">
382+
<span className="font-bold text-xs">Expected Output: </span>
383+
<span className="text-primary-400 tracking-wide text-xs">
380384
{example.expected_output}
381385
</span>
382386
</div>
383-
<span className="font-bold">Explanation: </span>
384-
<span className="text-primary-400 tracking-wide">
387+
<span className="font-bold text-xs">Explanation: </span>
388+
<span className="text-primary-400 tracking-wide text-xs">
385389
{example.explanation}
386390
</span>
387391
<br />
@@ -394,20 +398,20 @@ const Question = ({
394398
className="mb-3"
395399
>
396400
{showAnswer ? "Hide" : "Show"} Answer
397-
{showAnswer ? " " : " "}
401+
{showAnswer ? " " : " "}
398402
</Button>
399403
{showAnswer && question?.solution && (
400404
<div className="h-[50px] text-sm">
401405
<span className="text-xs italic">
402406
We currently only support JavaScript. Sorry!
403407
</span>
404-
<SyntaxHighlighter language="javascript">
408+
<SyntaxHighlighter language="javascript" class="text-xs">
405409
{question?.solution}
406410
</SyntaxHighlighter>
407411
</div>
408412
)}
409413
</span>
410-
<div className="row-span-1 flex flex-col bg-primary-800 rounded-md h-full max-h-[90%] min-h-[80%] overflow-y-auto">
414+
<div className="row-span-1 flex flex-col bg-primary-800 rounded-md h-full max-h-[90%] overflow-y-auto">
411415
{isLoading && (
412416
<div className="flex justify-center p-2">
413417
<MoonLoader size={20} />

frontend/src/app/common/Layout.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import Sidebar from "./Sidebar";
33

44
const Layout = ({ children }: { children: ReactNode }) => {
55
return (
6-
<div className="flex h-full overflow-y-auto">
7-
<Sidebar/>
8-
<div className="w-full">{children}</div>
6+
<div className="flex flex-row">
7+
<Sidebar />
8+
<div className="w-full h-full">{children}</div>
99
</div>
1010
);
11-
}
11+
};
1212

13-
export default Layout;
13+
export default Layout;

0 commit comments

Comments
 (0)