Skip to content

Commit 9c3aba9

Browse files
authored
Merge pull request #61 from CS3219-AY2425S1/feat/collaboration-page
Collaboration Page Skeletion (No functionality)
2 parents f2c99e5 + c978a40 commit 9c3aba9

File tree

9 files changed

+224
-8
lines changed

9 files changed

+224
-8
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import Navbar from '@/components/navbar/Navbar';
33

4-
const MainLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
4+
const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
55
return (
66
<>
77
<Navbar />
@@ -10,4 +10,4 @@ const MainLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
1010
);
1111
};
1212

13-
export default MainLayout;
13+
export default AdminLayout;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from 'react';
2+
import Navbar from '@/components/navbar/Navbar';
3+
4+
const CollaborationLayout: React.FC<{ children: React.ReactNode }> = ({
5+
children,
6+
}) => {
7+
return (
8+
<>
9+
<Navbar />
10+
<main>{children}</main>
11+
</>
12+
);
13+
};
14+
15+
export default CollaborationLayout;
Lines changed: 148 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,152 @@
1-
import React from 'react';
1+
'use client';
22

3-
type Props = {};
3+
import ProblemDescriptionPanel from '@/components/problems/ProblemDescriptionPanel';
4+
import ProblemTable from '@/components/problems/ProblemTable';
5+
import { Button } from '@/components/ui/button';
6+
import {
7+
Select,
8+
SelectContent,
9+
SelectItem,
10+
SelectTrigger,
11+
SelectValue,
12+
} from '@/components/ui/select';
13+
import { Textarea } from '@/components/ui/textarea';
14+
import { useFilteredProblems } from '@/hooks/useFilteredProblems';
15+
import { DEFAULT_CODE, SUPPORTED_PROGRAMMING_LANGUAGES } from '@/lib/constants';
16+
import { Problem } from '@/types/types';
17+
import { UserCircle } from 'lucide-react';
18+
import React, { useCallback, useEffect, useRef, useState } from 'react';
419

5-
const page = (props: Props) => {
6-
return <div>This is the collaboration page</div>;
20+
const CollaborationPage = () => {
21+
const [selectionProblem, setSelectionProblem] = useState<Problem | null>(
22+
null,
23+
);
24+
const [code, setCode] = useState(DEFAULT_CODE);
25+
const [language, setLanguage] = useState(SUPPORTED_PROGRAMMING_LANGUAGES[0]);
26+
const { problems, isLoading } = useFilteredProblems();
27+
28+
// Layout states
29+
const [leftWidth, setLeftWidth] = useState(50);
30+
const isDragging = useRef(false);
31+
const containerRef = useRef<HTMLDivElement>(null);
32+
33+
// Handle dragging of the divider
34+
const handleMouseDown = useCallback(() => {
35+
isDragging.current = true;
36+
}, []);
37+
38+
const handleMouseUp = useCallback(() => {
39+
isDragging.current = false;
40+
}, []);
41+
42+
const handleMouseMove = useCallback((e: MouseEvent) => {
43+
if (!isDragging.current || !containerRef.current) return;
44+
const containerRect = containerRef.current.getBoundingClientRect();
45+
const newLeftWidth =
46+
((e.clientX - containerRect.left) / containerRect.width) * 100;
47+
setLeftWidth(Math.max(20, Math.min(80, newLeftWidth)));
48+
}, []);
49+
50+
useEffect(() => {
51+
document.addEventListener('mousemove', handleMouseMove);
52+
document.addEventListener('mouseup', handleMouseUp);
53+
return () => {
54+
document.removeEventListener('mousemove', handleMouseMove);
55+
document.removeEventListener('mouseup', handleMouseUp);
56+
};
57+
}, [handleMouseMove, handleMouseUp]);
58+
59+
// Handle selection of a problem
60+
const handleCallback = (id: number) => {
61+
const problem = problems.find((p) => p._id === id);
62+
if (problem) {
63+
setSelectionProblem(problem);
64+
}
65+
};
66+
67+
return (
68+
<div
69+
className="flex h-screen overflow-hidden bg-gray-900 p-6 pt-24 text-gray-100"
70+
ref={containerRef}
71+
>
72+
<div
73+
className="h-full overflow-y-auto p-6"
74+
style={{ width: `${leftWidth}%` }}
75+
>
76+
{selectionProblem ? (
77+
<ProblemDescriptionPanel
78+
problem={selectionProblem}
79+
resetQuestion={() => setSelectionProblem(null)}
80+
/>
81+
) : (
82+
<>
83+
<h2 className="mb-4 text-2xl font-bold">Choose a question</h2>
84+
<ProblemTable
85+
problems={problems}
86+
isLoading={isLoading}
87+
rowCallback={handleCallback}
88+
/>
89+
</>
90+
)}
91+
</div>
92+
93+
<div
94+
className="flex w-2 cursor-col-resize items-center justify-center bg-gray-600 transition-colors duration-200 hover:bg-gray-500"
95+
onMouseDown={handleMouseDown}
96+
>
97+
<div className="h-8 w-1 rounded-full bg-gray-400" />
98+
</div>
99+
100+
<div
101+
className="flex h-full flex-col overflow-y-auto bg-gray-800 p-6"
102+
style={{ width: `${100 - leftWidth}%` }}
103+
>
104+
<div className="mb-4 flex justify-between">
105+
<Select value={language} onValueChange={setLanguage}>
106+
<SelectTrigger className="w-32">
107+
<SelectValue placeholder="Select Language" />
108+
</SelectTrigger>
109+
<SelectContent>
110+
{SUPPORTED_PROGRAMMING_LANGUAGES.map((lang) => (
111+
<SelectItem key={lang} value={lang}>
112+
{lang}
113+
</SelectItem>
114+
))}
115+
</SelectContent>
116+
</Select>
117+
<div className="flex">
118+
<Button
119+
variant="ghost"
120+
size="icon"
121+
className="relative h-8 w-8 rounded-full"
122+
onClick={() => {
123+
console.log('Clicked user');
124+
}}
125+
>
126+
<UserCircle className="h-6 w-6 text-gray-300" />
127+
</Button>
128+
<Button
129+
variant="ghost"
130+
size="icon"
131+
className="relative h-8 w-8 rounded-full"
132+
onClick={() => {
133+
console.log('Clicked user');
134+
}}
135+
>
136+
<UserCircle className="h-6 w-6 text-gray-300" />
137+
</Button>
138+
</div>
139+
</div>
140+
141+
<Textarea
142+
value={code}
143+
onChange={(e) => setCode(e.target.value)}
144+
className="flex-grow overflow-y-auto bg-gray-900 font-mono text-gray-100"
145+
style={{ resize: 'none', height: 'calc(100% - 3rem)' }}
146+
/>
147+
</div>
148+
</div>
149+
);
7150
};
8151

9-
export default page;
152+
export default CollaborationPage;

peerprep-fe/src/app/signup/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export default function SignUpPage() {
2424
e.preventDefault();
2525
setError('');
2626

27+
// const type = 'user';
2728
if (password !== confirmPassword) {
2829
router.push('/signup');
2930
setError('Passwords do not match');

peerprep-fe/src/components/navbar/Navbar.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,19 @@ import { useAuthStore } from '@/state/useAuthStore';
1616
import { useQuestionStore } from '@/state/useQuestionStore';
1717
import { PreMatch } from '@/components/dialogs/PreMatch';
1818
import { usePathname } from 'next/navigation';
19+
import { useRouter } from 'next/navigation';
1920

2021
export default function Navbar() {
2122
const { isAuth, clearAuth, user } = useAuthStore();
2223
const { toggleDialogOpen } = useQuestionStore();
2324
const path = usePathname();
25+
const router = useRouter();
2426

2527
const handleLogout = async () => {
2628
const res = await logout();
2729
if (res) {
2830
clearAuth();
31+
router.push('/');
2932
return;
3033
}
3134
};
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { Problem } from '@/types/types';
2+
import React from 'react';
3+
import { Button } from '../ui/button';
4+
5+
type Props = {
6+
problem: Problem;
7+
resetQuestion: () => void; // replace with something more generic
8+
};
9+
10+
const ProblemDescriptionPanel = ({ problem, resetQuestion }: Props) => {
11+
return (
12+
<>
13+
<div className="flex justify-between">
14+
<h2 className="mb-4 text-2xl font-bold">{problem.title}</h2>
15+
<Button
16+
variant="outline"
17+
className="border-gray-700 bg-gray-800"
18+
onClick={() => resetQuestion()}
19+
>
20+
Reset
21+
</Button>
22+
</div>
23+
<p className="mb-4">{problem.description}</p>
24+
{problem.examples.map((example, index) => (
25+
<React.Fragment key={index}>
26+
<h3 className="mb-2 text-xl font-semibold">Example {index + 1}:</h3>
27+
<pre className="mb-4 text-wrap rounded bg-gray-800 p-4">
28+
{example}
29+
</pre>
30+
</React.Fragment>
31+
))}
32+
<h3 className="mb-2 text-xl font-semibold">Constraints:</h3>
33+
<p>{problem.constraints ?? 'None'}</p>
34+
</>
35+
);
36+
};
37+
38+
export default ProblemDescriptionPanel;

peerprep-fe/src/components/problems/ProblemRow.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ interface Props {
2727
handleEdit:
2828
| ((problem: Problem) => Promise<AxiosResponse<unknown, unknown>>)
2929
| undefined;
30+
rowCallback?: (id: number) => void;
3031
}
3132

3233
export default function ProblemRow({
3334
problem,
3435
showActions,
3536
handleDelete,
3637
handleEdit,
38+
rowCallback,
3739
}: Props) {
3840
const [isDialogOpen, setIsDialogOpen] = useState(false);
3941
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
@@ -68,7 +70,11 @@ export default function ProblemRow({
6870
</td>
6971
<td
7072
className="cursor-pointer px-4 py-2 font-medium transition-colors hover:text-blue-500"
71-
onClick={() => setIsDialogOpen(true)}
73+
onClick={
74+
rowCallback
75+
? () => rowCallback(problem._id)
76+
: () => setIsDialogOpen(true)
77+
}
7278
>
7379
{problem.title}
7480
</td>

peerprep-fe/src/components/problems/ProblemTable.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface ProblemTableProps {
1414
handleEdit?:
1515
| ((problem: Problem) => Promise<AxiosResponse<unknown, unknown>>)
1616
| undefined;
17+
rowCallback?: (id: number) => void;
1718
}
1819

1920
export default function ProblemTable({
@@ -22,6 +23,7 @@ export default function ProblemTable({
2223
showActions = false,
2324
handleDelete,
2425
handleEdit,
26+
rowCallback,
2527
}: ProblemTableProps) {
2628
return (
2729
<div className="overflow-x-auto">
@@ -64,6 +66,7 @@ export default function ProblemTable({
6466
showActions={showActions}
6567
handleDelete={handleDelete}
6668
handleEdit={handleEdit}
69+
rowCallback={rowCallback}
6770
/>
6871
))}
6972
</tbody>

peerprep-fe/src/lib/constants.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,10 @@ export const INITIAL_PROBLEM_DATA: Problem = {
2121
tags: [],
2222
title_slug: '',
2323
};
24+
25+
export const SUPPORTED_PROGRAMMING_LANGUAGES = ['C++', 'Java', 'Python'];
26+
27+
export const DEFAULT_CODE = `int main() {
28+
"Hello World!";
29+
return 0;
30+
}`;

0 commit comments

Comments
 (0)