diff --git a/backend/collaboration-service/src/models/collab.model.ts b/backend/collaboration-service/src/models/collab.model.ts index e58c5cb1ab..0ed9275715 100644 --- a/backend/collaboration-service/src/models/collab.model.ts +++ b/backend/collaboration-service/src/models/collab.model.ts @@ -2,6 +2,7 @@ import { Schema } from 'mongoose' import { CollabDto } from '../types/CollabDto' import { LanguageMode } from '@repo/collaboration-types' import chatModelSchema from './chat.model' +import executionResultSchema from './result.model' const collabSchema = new Schema({ language: { @@ -14,8 +15,9 @@ const collabSchema = new Schema({ required: false, }, executionResult: { - type: String, + type: executionResultSchema, required: false, + default: {}, }, chatHistory: { type: [chatModelSchema], diff --git a/backend/collaboration-service/src/models/collab.repository.ts b/backend/collaboration-service/src/models/collab.repository.ts index 693f6af37c..95e28ba268 100644 --- a/backend/collaboration-service/src/models/collab.repository.ts +++ b/backend/collaboration-service/src/models/collab.repository.ts @@ -1,7 +1,7 @@ import { Model, model } from 'mongoose' import { CollabDto } from '../types' import collabSchema from './collab.model' -import { LanguageMode, ChatModel } from '@repo/collaboration-types' +import { LanguageMode, ChatModel, ResultModel } from '@repo/collaboration-types' const collabModel: Model = model('collab', collabSchema) @@ -32,6 +32,6 @@ export async function saveCode(id: string, code: string) { return collabModel.updateOne({ _id: id }, { $set: { code: code } }) } -export async function saveResult(id: string, result: string) { +export async function saveResult(id: string, result: ResultModel) { return collabModel.updateOne({ _id: id }, { $set: { executionResult: result } }) } diff --git a/backend/collaboration-service/src/models/result.model.ts b/backend/collaboration-service/src/models/result.model.ts new file mode 100644 index 0000000000..de45737bc8 --- /dev/null +++ b/backend/collaboration-service/src/models/result.model.ts @@ -0,0 +1,31 @@ +import { Schema } from 'mongoose' +import { ResultModel } from '@repo/collaboration-types' + +const executionResultSchema = new Schema({ + time: { + type: String, + required: false, + }, + status: { + type: Object, + required: false, + }, + stderr: { + type: String, + required: false, + }, + compile_output: { + type: String, + required: false, + }, + stdout: { + type: String, + required: false, + }, + testIndex: { + type: Number, + required: false, + }, +}) + +export default executionResultSchema diff --git a/backend/collaboration-service/src/services/socketio.service.ts b/backend/collaboration-service/src/services/socketio.service.ts index d1e077f631..860b453eda 100644 --- a/backend/collaboration-service/src/services/socketio.service.ts +++ b/backend/collaboration-service/src/services/socketio.service.ts @@ -1,10 +1,11 @@ import loggerUtil from '../common/logger.util' import { Server as IOServer, Socket } from 'socket.io' import { completeCollaborationSession } from './collab.service' -import { updateChatHistory, updateLanguage } from '../models/collab.repository' +import { saveResult, updateChatHistory, updateLanguage } from '../models/collab.repository' import { LanguageMode, ChatModel } from '@repo/collaboration-types' import { SubmissionResponseDto } from '../types' -import { IResponse, ISubmission } from '@repo/submission-types' +import { ISubmission } from '@repo/submission-types' +import { ResultModel } from '@repo/collaboration-types' import { submitCode } from '../controllers/collab.controller' export class WebSocketConnection { @@ -25,17 +26,17 @@ export class WebSocketConnection { socket.join(roomId) this.io.to(roomId).emit('user-connected', name) if (this.languages.has(roomId)) { - socket.emit('update-language', this.languages.get(roomId)) + socket.emit('update-language', this.languages.get(roomId), false) } }) - socket.on('send_message', (data: ChatModel) => { + socket.on('send_message', async (data: ChatModel) => { + await updateChatHistory(data.roomId, data) this.io.to(data.roomId).emit('receive_message', data) - updateChatHistory(data.roomId, data) }) socket.on('change-language', async (language: string) => { - this.io.to(roomId).emit('update-language', language) + this.io.to(roomId).emit('update-language', language, true) this.languages.set(roomId, language) await updateLanguage(roomId, language as LanguageMode) }) @@ -45,8 +46,16 @@ export class WebSocketConnection { try { const dto: SubmissionResponseDto = await submitCode(data) const { stdout, status, time, stderr, compile_output } = dto - const response: IResponse = { stdout, status, time, stderr, compile_output } + const response: ResultModel = { + stdout, + status, + time, + stderr, + compile_output, + testIndex: data.testIndex, + } this.io.to(roomId).emit('code-executed', response, data.expected_output) + await saveResult(roomId, response) } catch (err) { this.io.to(roomId).emit('code-executed', { error: err }) } diff --git a/backend/collaboration-service/src/types/CollabDto.ts b/backend/collaboration-service/src/types/CollabDto.ts index cc7be4ed8d..5b95dabc93 100644 --- a/backend/collaboration-service/src/types/CollabDto.ts +++ b/backend/collaboration-service/src/types/CollabDto.ts @@ -10,7 +10,7 @@ import { ValidationError, } from 'class-validator' import { Type } from 'class-transformer' -import { LanguageMode, ChatModel, ICollabCreateSessionDto } from '@repo/collaboration-types' +import { LanguageMode, ChatModel, ICollabCreateSessionDto, ResultModel } from '@repo/collaboration-types' import 'reflect-metadata' export class CollabDto { @@ -25,8 +25,8 @@ export class CollabDto { @IsString() code: string - @IsString() - executionResult: string + @Type(() => ResultModel) + executionResult: ResultModel @IsArray() @ValidateNested({ each: true }) @@ -41,7 +41,7 @@ export class CollabDto { matchId: string, language: LanguageMode, code: string, - executionResult: string, + executionResult: ResultModel, chatHistory: ChatModel[] ) { this.matchId = matchId @@ -53,7 +53,7 @@ export class CollabDto { } static fromCreateRequest({ body: { matchId, language } }: ITypedBodyRequest): CollabDto { - return new CollabDto(matchId, language, '', '', []) + return new CollabDto(matchId, language, '', {}, []) } static fromModel({ matchId, language, code, executionResult, chatHistory }: CollabDto): CollabDto { diff --git a/backend/matching-service/src/controllers/matching.controller.ts b/backend/matching-service/src/controllers/matching.controller.ts index 4b23634417..6344bab031 100644 --- a/backend/matching-service/src/controllers/matching.controller.ts +++ b/backend/matching-service/src/controllers/matching.controller.ts @@ -69,7 +69,14 @@ export async function handleCreateMatch(data: IMatch, ws1: string, ws2: string): wsConnection.sendMessageToUser(ws2, JSON.stringify({ type: WebSocketMessageType.DUPLICATE })) } - const question = await getRandomQuestion(data.category, convertComplexityToSortedComplexity(data.complexity)) + let question + + try { + question = await getRandomQuestion(data.category, convertComplexityToSortedComplexity(data.complexity)) + } catch (err) { + logger.error('[Matching-Service] Failed to get random question' + err) + return + } if (!question) { logger.error('[Matching-Service] Could not get a random question') diff --git a/frontend/components/customs/difficulty-label.tsx b/frontend/components/customs/difficulty-label.tsx index bea141d464..31a7555142 100644 --- a/frontend/components/customs/difficulty-label.tsx +++ b/frontend/components/customs/difficulty-label.tsx @@ -1,3 +1,4 @@ +import { capitalizeFirstLowerRest } from '@/util/string-modification' import CustomLabel from '../ui/label' import { Complexity } from '@repo/user-types' @@ -28,5 +29,5 @@ export const DifficultyLabel: FC = ({ complexity }) => { bgColor = 'bg-theme-100' } - return + return } diff --git a/frontend/components/dashboard/new-session.tsx b/frontend/components/dashboard/new-session.tsx index 5ff55d873f..515c07d052 100644 --- a/frontend/components/dashboard/new-session.tsx +++ b/frontend/components/dashboard/new-session.tsx @@ -13,6 +13,7 @@ import { toast } from 'sonner' import { addUserToMatchmaking } from '../../services/matching-service-api' import CustomModal from '../customs/custom-modal' import Loading from '../customs/loading' +import { capitalizeFirstLowerRest } from '@/util/string-modification' export const NewSession = () => { const router = useRouter() @@ -20,12 +21,12 @@ export const NewSession = () => { const TopicOptions = Object.values(Category).map((category) => ({ value: category, - label: category.charAt(0).toUpperCase() + category.toLocaleLowerCase().slice(1), + label: capitalizeFirstLowerRest(category), })) const ComplexityOptions = Object.values(Complexity).map((complexity) => ({ value: complexity, - label: complexity.charAt(0).toUpperCase() + complexity.toLocaleLowerCase().slice(1), + label: capitalizeFirstLowerRest(complexity), })) const { data: session } = useSession() @@ -52,7 +53,7 @@ export const NewSession = () => { const handleMatchmaking = async () => { if (!selectedTopic || !selectedComplexity) { - toast.error('Please select a topic and complexity level to start matchmaking.') + toast.error('Please select a category and complexity level to start matchmaking.') return } setTimeElapsed(0) @@ -171,11 +172,11 @@ export const NewSession = () => {
Start a New Session

- Choose a Topic and Complexity level to start your collaborative + Choose a Category and Complexity level to start your collaborative coding session!

-

Topic

+

Category

@@ -43,7 +44,7 @@ export default function ResumeSession({ match, isOngoing }: IResumeSessionProps) diff --git a/frontend/pages/code/[id]/chat.tsx b/frontend/pages/code/[id]/chat.tsx index 79e33cb347..16cbf18b61 100644 --- a/frontend/pages/code/[id]/chat.tsx +++ b/frontend/pages/code/[id]/chat.tsx @@ -1,97 +1,50 @@ -import { FC, RefObject, useEffect, useRef, useState } from 'react' - +import { FC, useEffect, useRef, useState } from 'react' import { useSession } from 'next-auth/react' -import { useRouter } from 'next/router' -import * as socketIO from 'socket.io-client' -import { getChatHistory } from '@/services/collaboration-service-api' import { ChatModel } from '@repo/collaboration-types' -import { toast } from 'sonner' const formatTimestamp = (timestamp: string) => { const date = new Date(timestamp) return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: true }).toUpperCase() } -const Chat: FC<{ socketRef: RefObject; isViewOnly: boolean }> = ({ socketRef, isViewOnly }) => { - const [chatData, setChatData] = useState() +const Chat: FC<{ chatData: ChatModel[]; isViewOnly: boolean; handleSendMessage: (msg: string) => void }> = ({ + chatData, + isViewOnly, + handleSendMessage, +}) => { const chatEndRef = useRef(null) const { data: session } = useSession() - const router = useRouter() const [value, setValue] = useState('') - const { id: roomId } = router.query - - useEffect(() => { - ;(async () => { - const matchId = router.query.id as string - if (!matchId) { - return - } - const response = await getChatHistory(matchId) - if (!response) { - toast.error('Failed to fetch chat history') - } - setChatData(response) - })() - }, [router]) - - useEffect(() => { - if (isViewOnly) return - if (socketRef?.current) { - socketRef.current.on('receive_message', (data: ChatModel) => { - setChatData((prev) => { - return [...(prev ?? []), data] - }) - }) - } - }, [socketRef, isViewOnly]) const getChatBubbleFormat = (currUser: string, type: 'label' | 'text') => { let format = '' if (currUser === session?.user.username) { format = 'items-end ml-5' - // Add more format based on the type if (type === 'text') { format += ' bg-theme-600 rounded-xl text-white' } } else { format = 'items-start text-left mr-5' - // Add more format based on the type if (type === 'text') { format += ' bg-slate-100 rounded-xl p-2 text-slate-900' } } - return format } const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && e.currentTarget.value.trim() !== '') { handleSendMessage(e.currentTarget.value) + setValue('') e.currentTarget.value = '' } } - const handleSendMessage = (message: string) => { - if (!session || !socketRef?.current) return - if (message.trim()) { - const msg: ChatModel = { - message: message, - senderId: session.user.username, - createdAt: new Date(), - roomId: roomId as string, - } - socketRef.current.emit('send_message', msg) - } - setValue('') - } - useEffect(() => { - if (isViewOnly) return - if (chatEndRef.current) { chatEndRef.current.scrollIntoView({ behavior: 'smooth' }) } - }, [chatData, isViewOnly]) + }, [chatData]) return ( <> diff --git a/frontend/pages/code/[id]/index.tsx b/frontend/pages/code/[id]/index.tsx index 575fac96fb..d1895ec0ab 100644 --- a/frontend/pages/code/[id]/index.tsx +++ b/frontend/pages/code/[id]/index.tsx @@ -1,5 +1,5 @@ import { EndIcon, PlayIcon } from '@/assets/icons' -import { LanguageMode, getCodeMirrorLanguage } from '@repo/collaboration-types' +import { ChatModel, ICollabDto, LanguageMode, getCodeMirrorLanguage } from '@repo/collaboration-types' import { useEffect, useRef, useState } from 'react' import { Button } from '@/components/ui/button' @@ -21,13 +21,17 @@ import Chat from './chat' import io, { Socket } from 'socket.io-client' import UserAvatar from '@/components/customs/custom-avatar' import { toast } from 'sonner' -import { ISubmission, IResponse } from '@repo/submission-types' +import { ISubmission } from '@repo/submission-types' import { mapLanguageToJudge0 } from '@/util/language-mapper' import TestResult from '../test-result' import { Cross1Icon } from '@radix-ui/react-icons' +import { getChatHistory, getCollabHistory } from '@/services/collaboration-service-api' +import ReadOnlyCodeMirrorEditor from '../read-only-editor' +import { ResultModel } from '@repo/collaboration-types' +import { capitalizeFirstLowerRest } from '@/util/string-modification' const formatQuestionCategories = (cat: Category[]) => { - return cat.join(', ') + return cat.map((c) => capitalizeFirstLowerRest(c)).join(', ') } export default function Code() { @@ -37,36 +41,45 @@ export default function Code() { const editorRef = useRef<{ getText: () => string } | null>(null) const [editorLanguage, setEditorLanguage] = useState(LanguageMode.Javascript) const testTabs = ['Testcases', 'Test Results'] + const [chatData, setChatData] = useState([]) const [activeTestTab, setActiveTestTab] = useState(0) const [matchData, setMatchData] = useState() + const [collabData, setCollabData] = useState() const socketRef = useRef(null) const [isOtherUserOnline, setIsOtherUserOnline] = useState(true) const [isCodeRunning, setIsCodeRunning] = useState(false) const [activeTest, setActiveTest] = useState(0) - const [testResult, setTestResult] = useState<{ data: IResponse; expectedOutput: string } | undefined>(undefined) - const [isViewOnly, setIsViewOnly] = useState(false) + const [retry, setRetry] = useState(0) + const [testResult, setTestResult] = useState<{ data: ResultModel | undefined; expectedOutput: string } | undefined>( + undefined + ) + const [isViewOnly, setIsViewOnly] = useState(true) - const retrieveMatchDetails = async () => { - const matchId = router.query.id as string - if (!matchId) { - router.push('/') - return - } - const response = await getMatchDetails(matchId).catch((_) => { - toast.error('Match does not exists') - router.push('/') + const retrieveMatchDetails = async (matchId: string) => { + const response = await getMatchDetails(matchId).catch((err) => { + if (retry >= 3) { + router.push('/') + } else { + setRetry(retry + 1) + } }) if (response) { setMatchData(response) setIsViewOnly(response.isCompleted) + if (response.isCompleted) { + const collabResponse = await getCollabHistory(matchId) + setEditorLanguage(collabResponse?.language ?? LanguageMode.Javascript) + setCollabData(collabResponse) + } } } const { data: sessionData } = useSession() useEffect(() => { - retrieveMatchDetails() - }, []) + const matchId = router.query.id as string + retrieveMatchDetails(matchId) + }, [router.query.id, retry]) useEffect(() => { if (isViewOnly) return @@ -80,22 +93,26 @@ export default function Code() { }, }) - socketRef.current.on('connect', () => { + socketRef.current.on('connect', async () => { + console.log('hi') if (socketRef.current) { socketRef.current.emit('joinRoom', { roomId: id }) + setChatData((await getChatHistory(id as string)) ?? []) } }) - socketRef.current.on('update-language', (language: string) => { + socketRef.current.on('update-language', (language: string, showToast: boolean) => { setEditorLanguage(language as LanguageMode) - toast.success('Language mode updated') + if (showToast) { + toast.success('Language mode updated') + } }) socketRef.current.on('executing-code', () => { setIsCodeRunning(true) }) - socketRef.current.on('code-executed', (res: IResponse, expected_output: string) => { + socketRef.current.on('code-executed', (res: ResultModel, expected_output: string) => { setTestResult({ data: res, expectedOutput: expected_output }) setIsCodeRunning(false) setActiveTestTab(1) @@ -113,6 +130,10 @@ export default function Code() { } }) + socketRef.current.on('receive_message', (data: ChatModel) => { + setChatData((prev) => [...prev, data]) + }) + return () => { if (socketRef.current) { socketRef.current.disconnect() @@ -124,6 +145,18 @@ export default function Code() { setIsChatOpen(!isChatOpen) } + const handleSendMessage = (message: string) => { + if (message.trim()) { + const msg: ChatModel = { + message: message, + senderId: sessionData?.user.username ?? '', + createdAt: new Date(), + roomId: id as string, + } + socketRef.current?.emit('send_message', msg) + } + } + const handleLanguageModeSelect = (value: string) => { socketRef.current?.emit('change-language', value) } @@ -140,6 +173,7 @@ export default function Code() { source_code: code, expected_output: matchData?.question.testOutputs[activeTest] ?? '', stdin: '', + testIndex: activeTest, } socketRef.current?.emit('run-code', data) } @@ -218,7 +252,13 @@ export default function Code() { /> - {isChatOpen && } + {isChatOpen && ( + + )}
@@ -264,12 +304,18 @@ export default function Code() { className="w-max text-white bg-neutral-800 rounded-tl-lg" /> - + {isViewOnly ? ( + + ) : ( + + )} ) : ( - + )} diff --git a/frontend/pages/code/editor.tsx b/frontend/pages/code/editor.tsx index f07f251304..f57cd5f51d 100644 --- a/frontend/pages/code/editor.tsx +++ b/frontend/pages/code/editor.tsx @@ -15,10 +15,9 @@ import { indentWithTab } from '@codemirror/commands' interface IProps { roomId: string language: string - isViewOnly: boolean } -const CodeMirrorEditor = forwardRef(({ roomId, language, isViewOnly }: IProps, ref) => { +const CodeMirrorEditor = forwardRef(({ roomId, language }: IProps, ref) => { const editorContainerRef = useRef(null) // eslint-disable-next-line no-unused-vars const [provider, setProvider] = useState(null) @@ -78,7 +77,6 @@ const CodeMirrorEditor = forwardRef(({ roomId, language, isViewOnly }: IProps, r oneDark, compartment.of(javascript()), yCollab(ytext, wsProvider.awareness), - EditorView.editable.of(!isViewOnly), ], }) const view = new EditorView({ diff --git a/frontend/pages/code/read-only-editor.tsx b/frontend/pages/code/read-only-editor.tsx new file mode 100644 index 0000000000..c68bd2ce91 --- /dev/null +++ b/frontend/pages/code/read-only-editor.tsx @@ -0,0 +1,75 @@ +import React, { useEffect, useMemo, useRef, useState } from 'react' +import { Compartment, EditorState } from '@codemirror/state' +import { EditorView, keymap } from '@codemirror/view' +import { basicSetup } from 'codemirror' +import { useSession } from 'next-auth/react' +import { languages } from '@codemirror/language-data' +import { oneDark } from '@codemirror/theme-one-dark' +import { javascript } from '@codemirror/lang-javascript' +import { indentWithTab } from '@codemirror/commands' + +interface IProps { + language: string + code: string +} + +const ReadOnlyCodeMirrorEditor = ({ language, code }: IProps) => { + const editorContainerRef = useRef(null) + // eslint-disable-next-line no-unused-vars + const { data: session } = useSession() + const [editorView, setEditorView] = useState(null) + const compartment = useMemo(() => new Compartment(), []) + + useEffect(() => { + if (!editorView) return + ;(async () => { + const languageExt = languages.find((lang) => lang.alias.includes(language) || lang.name === language) + if (!languageExt) return + const data = await languageExt.load() + editorView.dispatch({ + effects: compartment.reconfigure(data), + }) + })() + }, [editorView, language]) + + useEffect(() => { + if (!session) return + const token = session.user.accessToken + if (!token) return undefined + if (editorContainerRef.current) { + const state = EditorState.create({ + doc: code, + extensions: [ + keymap.of([indentWithTab]), + basicSetup, + oneDark, + compartment.of(javascript()), + EditorView.editable.of(false), + ], + }) + const view = new EditorView({ + state, + parent: editorContainerRef.current, + }) + setEditorView(view) + return () => { + view.destroy() + } + } + return undefined + }, [editorContainerRef, code]) + + return ( +
+ ) +} + +export default ReadOnlyCodeMirrorEditor diff --git a/frontend/pages/code/test-result.tsx b/frontend/pages/code/test-result.tsx index 3947aa5b78..1176c9530c 100644 --- a/frontend/pages/code/test-result.tsx +++ b/frontend/pages/code/test-result.tsx @@ -1,34 +1,40 @@ -import { IResponse } from '@repo/submission-types' +import { ResultModel } from '@repo/collaboration-types' -export default function TestResult({ result, expectedOutput }: { result?: IResponse; expectedOutput: string }) { +export default function TestResult({ + result, + expectedOutput, +}: { + result?: ResultModel | undefined + expectedOutput: string +}) { if (!result) return
No test results yet
return (
{result.status?.id === 3 ? ( - {result.status.description} + {result?.status?.description} ) : ( - {result.status.description} + {result?.status?.description} )}

Runtime: {result.time} ms

- {result.status.id === 6 && result.compile_output && ( + {result?.status?.id === 6 && result?.compile_output && (
Error

- {result.compile_output} + {result?.compile_output}

)} - {result.stderr && ( + {result?.stderr && (
Error

- {result.stderr} + {result?.stderr}

)} - {(result.status.id === 3 || result.status.id == 4) && ( + {(result?.status?.id === 3 || result?.status?.id == 4) && (
Your Output = diff --git a/frontend/pages/questions/props.tsx b/frontend/pages/questions/props.tsx index a121f58f43..2e034d04e6 100644 --- a/frontend/pages/questions/props.tsx +++ b/frontend/pages/questions/props.tsx @@ -5,6 +5,7 @@ import { Category, Complexity } from '@repo/user-types' import CustomLabel from '@/components/ui/label' import { DifficultyLabel } from '@/components/customs/difficulty-label' import { ITestcase } from '@/types/question' +import { capitalizeFirstLowerRest } from '@/util/string-modification' const getColumns = (isAdmin: boolean): IDatatableColumn[] => { return [ @@ -22,7 +23,13 @@ const getColumns = (isAdmin: boolean): IDatatableColumn[] => { width: '40%', formatter: (values) => { const c = values.map((v: string) => ( - + )) return
{c}
}, diff --git a/frontend/pages/sessions/columns.tsx b/frontend/pages/sessions/columns.tsx index 1566e20615..1ac1c8172a 100644 --- a/frontend/pages/sessions/columns.tsx +++ b/frontend/pages/sessions/columns.tsx @@ -3,6 +3,7 @@ import { DifficultyLabel } from '@/components/customs/difficulty-label' import { Button } from '@/components/ui/button' import CustomLabel from '@/components/ui/label' import { IDatatableColumn, IRowData } from '@/types' +import { capitalizeFirstLowerRest } from '@/util/string-modification' import { EyeIcon } from 'lucide-react' import { NextRouter } from 'next/router' @@ -20,17 +21,24 @@ const convertTimestamp = (millis: number) => { export const columns: IDatatableColumn[] = [ { key: 'collaboratorName', - label: 'Name', + label: 'Collaborator', + offAutoCapitalize: true, }, { key: 'question', + offAutoCapitalize: true, }, { key: 'category', formatter: (value) => { return (
- +
) }, diff --git a/frontend/services/collaboration-service-api.ts b/frontend/services/collaboration-service-api.ts index df86ad722b..358c5ed5e6 100644 --- a/frontend/services/collaboration-service-api.ts +++ b/frontend/services/collaboration-service-api.ts @@ -1,4 +1,4 @@ -import { ChatModel } from '@repo/collaboration-types' +import { ChatModel, ICollabDto } from '@repo/collaboration-types' import axiosClient from './axios-middleware' const axiosInstance = axiosClient.collaborationServiceAPI @@ -11,3 +11,12 @@ export const getChatHistory = async (matchId: string): Promise => { + try { + const response: ICollabDto = await axiosInstance.get(`/collab/${matchId}`) + return response + } catch (error) { + console.error(error) + } +} diff --git a/frontend/util/string-modification.ts b/frontend/util/string-modification.ts index 74f25fb3cb..94c4fdedc6 100644 --- a/frontend/util/string-modification.ts +++ b/frontend/util/string-modification.ts @@ -1,3 +1,7 @@ export const capitalizeFirst = (str: string): string => { return str.charAt(0).toUpperCase() + str.slice(1) } + +export const capitalizeFirstLowerRest = (value: string) => { + return value.charAt(0).toUpperCase() + value.toLocaleLowerCase().slice(1) +} diff --git a/packages/collaboration-types/src/ICollabDto.ts b/packages/collaboration-types/src/ICollabDto.ts index 7b6e8a0aca..cbc5e39f35 100644 --- a/packages/collaboration-types/src/ICollabDto.ts +++ b/packages/collaboration-types/src/ICollabDto.ts @@ -1,5 +1,6 @@ import { ChatModel } from './ChatModel' import { LanguageMode } from './LanguageMode' +import { ResultModel } from './ResultModel' export interface ICollabCreateSessionDto { matchId: string @@ -8,6 +9,6 @@ export interface ICollabCreateSessionDto { export interface ICollabDto extends ICollabCreateSessionDto { code: string - executionResult: string + executionResult: ResultModel chatHistory: ChatModel[] } diff --git a/packages/collaboration-types/src/ResultModel.ts b/packages/collaboration-types/src/ResultModel.ts new file mode 100644 index 0000000000..1aa80978a2 --- /dev/null +++ b/packages/collaboration-types/src/ResultModel.ts @@ -0,0 +1,8 @@ +export class ResultModel { + time?: string + status?: { id: number; description: string } + stdout?: string + stderr?: string + compile_output?: string + testIndex?: number +} diff --git a/packages/collaboration-types/src/index.ts b/packages/collaboration-types/src/index.ts index 482ea9c145..d3e4285059 100644 --- a/packages/collaboration-types/src/index.ts +++ b/packages/collaboration-types/src/index.ts @@ -1,3 +1,4 @@ export * from './LanguageMode' export * from './ICollabDto' export * from './ChatModel' +export * from './ResultModel' diff --git a/packages/submission-types/src/ISubmission.ts b/packages/submission-types/src/ISubmission.ts index a1fdc217ca..672bfebb84 100644 --- a/packages/submission-types/src/ISubmission.ts +++ b/packages/submission-types/src/ISubmission.ts @@ -3,4 +3,5 @@ export interface ISubmission { source_code: string stdin: string expected_output: string + testIndex: number }