Skip to content

Commit

Permalink
Merge pull request #63 from brendantwh/main
Browse files Browse the repository at this point in the history
Minor changes to matching + frontend
  • Loading branch information
brendantwh authored Nov 7, 2024
2 parents c0e9fe0 + a91dbbe commit c39c9b9
Show file tree
Hide file tree
Showing 73 changed files with 135 additions and 97 deletions.
18 changes: 4 additions & 14 deletions backend/matching-service/controllers/matchingController.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const EventEmitter = require('events');
const uuid = require("uuid");

const QUEUE_TIME = 30000;
const BATCH_INTERVAL = 10000;
const BATCH_INTERVAL = 3000;

// Kafka setup
const kafka = new Kafka({
Expand Down Expand Up @@ -92,6 +92,8 @@ const matchmakeUser = async (userId, userName, questions) => {
}

const dequeueUser = async (userId) => {
userQueueMap.delete(userId);
dequeued.set(userId, true);
eventEmitter.emit(`dequeue-${userId}`);
}

Expand Down Expand Up @@ -141,19 +143,7 @@ const batchProcess = () => {
let questionDict = new Map();
let unmatchedUsers = new Map();

// Remove duplicate users, keeping only the most recent instance
let uniqueUsers = new Map();
for (const user of batch) {
if (uniqueUsers.has(user.userId)) {
if (user.enqueueTime > uniqueUsers.get(user.userId).enqueueTime) {
uniqueUsers.set(user.userId, user);
}
} else {
uniqueUsers.set(user.userId, user);
}
}

batch = Array.from(uniqueUsers.values());


batch.forEach((user) => {
if (!dequeued.has(user.userId)) {
Expand Down
5 changes: 3 additions & 2 deletions frontend/app/(authenticated)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,14 @@ export default function AuthenticatedLayout({
const authenticateUser = async () => {
const token = getCookie('token');
if (!token || await isTokenExpired(token)) {
deleteAllCookies();
router.push('/auth/login');
return;
}

// if non-admin user tries to access repo, redirect user to question page
// if non-admin user tries to access repo, redirect user to question page
if (pathname.includes('/question-repo') && !isUserAdmin()) {
router.push('/questions');
router.replace('/questions');
return;
}
};
Expand Down
73 changes: 41 additions & 32 deletions frontend/app/(authenticated)/profile/question-history/code/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
"use client";

import { useEffect, useRef, useState } from 'react';
import { Suspense, useEffect, useRef, useState } from 'react';
import { Copy, Flag, MessageSquareText } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Toaster } from "@/components/ui/sonner"
import { useSearchParams } from 'next/navigation';
import { getCookie, setCookie } from '@/app/utils/cookie-manager';
import { ScrollArea } from "@/components/ui/scroll-area"
import { Badge } from '@/components/ui/badge';
import { Badge, BadgeProps } from '@/components/ui/badge';
import Editor from "@monaco-editor/react";
import { toast } from "sonner"
import Markdown from 'react-markdown'
import SessionLoading from '@/app/session/loading';

type Question = {
id: number;
Expand All @@ -22,9 +23,10 @@ type Question = {
}

function getTimeAgo(attemptDate: Date | null) {
if (!attemptDate) return "N/A";
const now = new Date();

const diffInMs = now - attemptDate; // Difference in milliseconds
const diffInMs = now.getTime() - attemptDate.getTime(); // Difference in milliseconds
const diffInMinutes = Math.floor(diffInMs / (1000 * 60));
const diffInHours = Math.floor(diffInMs / (1000 * 60 * 60));
const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24));
Expand All @@ -40,7 +42,7 @@ function getTimeAgo(attemptDate: Date | null) {
}
}

export default function CodeViewer() {
function CodeViewerContent() {
const searchParams = useSearchParams();
const questionId = searchParams.get("questionId");
const [attemptDate, setAttemptDate] = useState<Date | null>(null);
Expand Down Expand Up @@ -103,7 +105,8 @@ export default function CodeViewer() {
setCode(JSON.parse(data.code))
setLanguage(data.language)
} catch (err) {
console.error(err.message);
const errorMessage = err instanceof Error ? err.message : 'An unknown error occurred';
console.error(errorMessage);
toast.dismiss();
toast.error("Failed to load the code. Please try again later.");
}
Expand All @@ -112,24 +115,24 @@ export default function CodeViewer() {
fetchAttemptDetails();
}, [questionId]);

const handleEditorDidMount = (editor) => {
editor.getDomNode().classList.add("my-custom-editor");

// Insert scoped tooltip styles
const style = document.createElement("style");
style.textContent = `
.my-custom-editor .monaco-tooltip {
z-index: 1000 !important;
position: absolute;
}
`;
document.head.appendChild(style);

// Cleanup on component unmount
return () => {
document.head.removeChild(style);
};
};
// const handleEditorDidMount = (editor: ) => {
// editor.getDomNode().classList.add("my-custom-editor");

// // Insert scoped tooltip styles
// const style = document.createElement("style");
// style.textContent = `
// .my-custom-editor .monaco-tooltip {
// z-index: 1000 !important;
// position: absolute;
// }
// `;
// document.head.appendChild(style);

// // Cleanup on component unmount
// return () => {
// document.head.removeChild(style);
// };
// };

const copyToClipboard = () => {
navigator.clipboard.writeText(code).then(() => {
Expand All @@ -139,13 +142,13 @@ export default function CodeViewer() {
};

return (
<div className="flex gap-4 min-h-screen px-10 pt-24 pb-5">
<div className="flex gap-4 min-h-screen px-10 pt-24 pb-5 text-primary">
{/* Left Panel: Question Details */}
<ScrollArea className="w-1/2 p-4 border rounded-lg shadow bg-white">
<h3 className="text-3xl font-serif font-large tracking-tight">
<ScrollArea className="w-1/2 p-6 border rounded-lg shadow bg-white">
<h3 className="text-2xl font-serif font-large tracking-tight">
{questionDetails?.title || ""}
</h3>
<div className="flex items-center gap-10 mt-8">
<div className="flex items-center gap-10 mt-3">
<div className="flex items-center gap-2">
<Flag className="h-4 w-4 text-icon" />
<Badge
Expand All @@ -159,7 +162,7 @@ export default function CodeViewer() {
<div className="flex items-center gap-2">
<MessageSquareText className="h-4 w-4 text-icon" />
{questionDetails?.category?.length > 0 &&
questionDetails?.category.map((category) => (
questionDetails?.category.map((category) => (
<Badge
key={category}
variant="category"
Expand All @@ -170,10 +173,7 @@ export default function CodeViewer() {
))}
</div>
</div>
<p className="mt-8 text-l text-foreground">
{questionDetails?.description || ""}
</p>
<Markdown className="mt-8 prose prose-zinc prose-code:bg-zinc-200 prose-code:px-1 prose-code:rounded prose-code:prose-pre:bg-inherit text-sm text-foreground proportional-nums">
<Markdown className="mt-8 prose prose-zinc prose-code:bg-zinc-200 prose-code:px-1 prose-code:rounded prose-code:prose-pre:bg-inherit text-base text-foreground proportional-nums">
{questionDetails?.description || ""}
</Markdown>
</ScrollArea>
Expand All @@ -199,6 +199,7 @@ export default function CodeViewer() {
readOnly: true,
minimap: { enabled: false },
lineNumbers: "on",
fontFamily: 'monospace',
fontSize: 14,
padding: { top: 10, bottom: 10 },
scrollBeyondLastLine: false,
Expand All @@ -215,3 +216,11 @@ export default function CodeViewer() {
</div>
);
}

export default function CodeViewer() {
return (
<Suspense fallback={<SessionLoading />}>
<CodeViewerContent />
</Suspense>
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client"

import { Badge } from "@/components/ui/badge";
import { Badge, BadgeProps } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card";
import { ColumnDef } from "@tanstack/react-table"
Expand Down Expand Up @@ -98,7 +98,7 @@ export const columns : ColumnDef<QuestionHistory>[]= [
const rowCategories = row.getValue(id);
console.log(selectedCategories);
console.log(rowCategories);
return selectedCategories.every(category => rowCategories.includes(category));
return selectedCategories.every((category: string) => (rowCategories as string[]).includes(category));
},
},
{
Expand Down Expand Up @@ -153,7 +153,7 @@ export const columns : ColumnDef<QuestionHistory>[]= [
)
},
accessorKey: "attemptTime",
cell: ({ row }) => <div className="flex items-center justify-center h-full">{ Math.ceil(row.getValue("attemptTime")/60)}</div>,
cell: ({ row }) => <div className="flex items-center justify-center h-full">{ Math.ceil(row.original.attemptTime/60)}</div>,
// Cell: ({ value }) => Math.floor(value / 60), // Convert time spent in seconds to minutes
},
{
Expand All @@ -170,7 +170,7 @@ export const columns : ColumnDef<QuestionHistory>[]= [
},
accessorKey: "attemptDate",
cell: ({ row }) => {
const attemptDate = row.getValue("attemptDate");
const attemptDate = row.original.attemptDate;
return new Date(attemptDate).toLocaleString("en-GB", {
day: "2-digit",
month: "2-digit",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default function UserQuestionHistory() {
const router = useRouter();
const [history, setHistory] = useState<QuestionHistory[]>([]);
const [loading, setLoading] = useState(true);
const userId = useRef(null);
const userId = useRef<string | null>(null);

// Fetch questions history from backend API
useEffect(() => {
Expand Down Expand Up @@ -104,7 +104,7 @@ export default function UserQuestionHistory() {
})
);

setHistory(mappedQuestions);
setHistory(mappedQuestions.reverse());
} catch (err) {
console.log("Error fetching questions from server:", err)
} finally {
Expand Down
42 changes: 19 additions & 23 deletions frontend/app/(authenticated)/questions/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -301,34 +301,30 @@ export default function Questions() {
};
}, []);

const ws1 = useRef<WebSocket | null>(null); // WebSocket reference
const handleCancel = useCallback(() => {
setIsMatching(false);
if (ws1.current === null || ws1.current.readyState === WebSocket.CLOSED || ws1.current.readyState === WebSocket.CLOSING) {
console.log("Connecting to web socket for matching service ...")
// Initialize WebSocket connection if not already matching
ws1.current = new WebSocket(process.env.NEXT_PUBLIC_MATCHING_API_URL || 'ws://localhost:3002/matching');
console.log(ws1.current.readyState)
}
ws1.current.onopen = () => {
console.log("WebSocket connection opened");

if (ws.current && ws.current.readyState === WebSocket.OPEN) {
const message = {
event: "dequeue",
userId: userInfo.current.id,
userId: getUserId(),
};

ws.current.send(JSON.stringify(message));
console.log("Sent dequeue request:", message);

ws.current?.send(JSON.stringify(message));
};
ws.current.close();
ws.current = null;
}
}, []);

ws1.current.onmessage = (event) => {
if (event.data == "Welcome to websocket server") {
console.log("receive welcome msg from websocket server")
return;
useEffect(() => {
return () => {
if (isMatching) {
handleCancel();
}
const message = JSON.parse(event.data);
console.log("message receive from websocket", message);
}
}, [])
};
}, [handleCancel, isMatching]);

useEffect(() => {
timeout.current = false;
Expand Down Expand Up @@ -497,7 +493,7 @@ export default function Questions() {
filteredQuestions.map((question) => (
<div id="qns" key={question.id} className="relative mr-2">
<Card
className="rounded-lg flex items-start border-none shadow-none p-4 w-full cursor-pointer hover:drop-shadow-question-card transition ease-in-out"
className="rounded-lg flex items-start border-none shadow-none p-4 w-full hover:drop-shadow-question-card transition ease-in-out"
onClick={() => setSelectedViewQuestion(question)}
>
<div className="flex-1">
Expand Down Expand Up @@ -615,7 +611,7 @@ export default function Questions() {
)}
</div>

<div className="flex flex-col h-max gap-2 px-2.5">
{/* <div className="flex flex-col h-max gap-2 px-2.5">
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>Past attempts</AccordionTrigger>
Expand All @@ -632,7 +628,7 @@ export default function Questions() {
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
</div> */}
</div>

<Dialog open={isMatchFoundDialogOpen}>
Expand Down
16 changes: 9 additions & 7 deletions frontend/app/auth/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,16 @@ export default function Login() {
const { accessToken, id, username, email, isAdmin, ...other } = responseData.data;

// set token
setCookie('token', accessToken, { 'max-age': '86400', 'path': '/', 'SameSite': 'Strict' });
const setCookiesAsync = async () => {
setCookie('token', accessToken, { 'max-age': '86400', 'path': '/', 'SameSite': 'Strict' });
setCookie('userId', id, { 'max-age': '86400', 'path': '/', 'SameSite': 'Strict' });
setCookie('username', username, { 'max-age': '86400', 'path': '/', 'SameSite': 'Strict' });
setCookie('isAdmin', isAdmin.toString(), { 'max-age': '86400', 'path': '/', 'SameSite': 'Strict' });
};

// set user info
setCookie('userId', id, { 'max-age': '86400', 'path': '/', 'SameSite': 'Strict' });
setCookie('username', username, { 'max-age': '86400', 'path': '/', 'SameSite': 'Strict' });
setCookie('isAdmin', isAdmin.toString(), { 'max-age': '86400', 'path': '/', 'SameSite': 'Strict' });

router.push("/questions");
await setCookiesAsync().then(() => {
router.push('/questions');
});
} catch (error) {
if (!isErrorSet) {
setError("Something went wrong. Please retry shortly.");
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function Landing() {
<div className="flex flex-col w-[350px] gap-1 items-start font-serif font-light text-3xl text-primary tracking-tight">
<p>Prep for your next interview with your Peers</p>
</div>
<div className="flex gap-4 pt-10 laptop:pt-20 items-center text-sm text-muted-foreground">
<div className="flex gap-4 pt-10 items-center text-sm text-muted-foreground">
<Button asChild variant="outline" size="lg" className="rounded-full px-6 border-brand-300 hover:border-brand-100 text-brand-600">
<Link href="/auth/login" className="">Login</Link>
</Button>
Expand Down
Loading

0 comments on commit c39c9b9

Please sign in to comment.