From 09ae4106a4b6ebcdb23b36ba6e23d1dd0fb5a7ea Mon Sep 17 00:00:00 2001 From: vivienherq Date: Tue, 9 Apr 2024 11:45:45 +0800 Subject: [PATCH 1/8] Merge changes from original grading service integration branch --- .../[id]/submissions/[sid]/page.tsx | 253 +++++------------- frontend/src/app/layout.tsx | 20 +- frontend/src/components/common/SideBar.tsx | 4 +- .../submission/FeedbackCodeEditor.tsx | 62 +++++ .../submission/FeedbackQuestion.tsx | 26 ++ .../components/submission/FeedbackTabs.tsx | 81 ++++++ .../helpers/grading-service/api-wrapper.ts | 59 ++++ frontend/src/types/grading-service.d.ts | 19 ++ 8 files changed, 324 insertions(+), 200 deletions(-) create mode 100644 frontend/src/components/submission/FeedbackCodeEditor.tsx create mode 100644 frontend/src/components/submission/FeedbackQuestion.tsx create mode 100644 frontend/src/components/submission/FeedbackTabs.tsx create mode 100644 frontend/src/helpers/grading-service/api-wrapper.ts create mode 100644 frontend/src/types/grading-service.d.ts diff --git a/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx b/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx index 997bc306..36748ce7 100644 --- a/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx +++ b/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx @@ -1,21 +1,15 @@ "use client"; -import { MutableRefObject, useRef } from "react"; -import Editor from "@monaco-editor/react"; -import { - Tabs, - Tab, - Card, - CardBody, - Spacer, - Divider, - Code, -} from "@nextui-org/react"; +import { useEffect, useState } from "react"; +import { Spacer, ButtonGroup, Button } from "@nextui-org/react"; import AssignmentService from "@/helpers/assignment-service/api-wrapper"; +import GradingService from "@/helpers/grading-service/api-wrapper"; import { useQuery } from "@tanstack/react-query"; import { notFound } from "next/navigation"; import DateUtils from "../../../../../utils/dateUtils"; -import * as monaco from "monaco-editor"; +import FeedbackCodeEditor from "@/components/submission/FeedbackCodeEditor"; +import FeedbackTabs from "@/components/submission/FeedbackTabs"; +import FeedbackQuestion from "@/components/submission/FeedbackQuestion"; interface Props { params: { @@ -24,46 +18,20 @@ interface Props { }; } -interface Item { - id: string; - label: string; - content: string[]; -} - export default function SubmissionPage({ params }: Props) { - const editorRef: MutableRefObject = - useRef(null); // const [value, setValue] = useState(""); // const [language, setLanguage] = useState("python"); + const userId = "1"; - const feedback = { - line: 2, - hints: ["Incorrect else block for if ( ((x % 2) == 1) )"], - }; + const [currentQuestion, setCurrentQuestion] = useState(0); - const newDecoration: monaco.editor.IModelDeltaDecoration[] = [ - { - range: new monaco.Range(feedback.line, 1, feedback.line, 1), // Highlight row 2 - options: { - isWholeLine: true, - className: "bg-yellow-200", - }, - }, - ]; + const [currentQuestionId, setCurrentQuestionId] = useState(""); - const onMount = (editor: monaco.editor.IStandaloneCodeEditor) => { - editorRef.current = editor; - editor.createDecorationsCollection(newDecoration); - editor.focus(); + const handleQuestionChange = (questionNumber: number, questionId: string) => { + setCurrentQuestion(questionNumber); + setCurrentQuestionId(questionId); }; - const code = `def is_odd(x): - if x % 2 == 1: - return False - else: - return True - `; - const { data: assignment, // isLoading, @@ -79,60 +47,54 @@ export default function SubmissionPage({ params }: Props) { }, }); + useEffect(() => { + if (assignment && assignment.questions) { + setCurrentQuestionId(assignment.questions[0]?.id ?? null); + } + }, [assignment]); + + const { data: submission, refetch: refetchSubmissions } = useQuery({ + queryKey: ["get-submissions", params.id, currentQuestionId], + queryFn: async () => { + const submission = + await GradingService.getSubmissionByQuestionIdAndStudentId({ + questionId: currentQuestionId, + studentId: userId, + }); + + return submission; + }, + }); + + useEffect(() => { + refetchSubmissions(); + }, [currentQuestionId]); + if (isError) { return notFound(); } - const tabs = [ - { - id: "testcases", - label: "Test Cases", - content: [ - "is_odd(1)", - "is_odd(2)", - "is_odd(3)", - "is_odd(0)", - "is_odd(-1)", - ], - }, - { - id: "feedback", - label: "Feedback", - content: [`Line ${feedback.line.toString()}: ${feedback.hints[0]}`], - }, - { - id: "grades", - label: "Grades", - content: [ - "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", - ], - }, - ]; - - const renderTabContent = (tabId: string, item: Item) => { - if (tabId === "testcases") { - return ( -
- {item.content.map((testcase: string) => ( - - {testcase} - - ))} -
- ); - } else if (tabId === "feedback") { - return item.content[0]; - } else if (tabId === "grades") { - return item.content[0]; - } + const feedback = { + line: 2, + hints: ["Incorrect else block for if ( ((x % 2) == 1) )"], }; return (
{assignment && ( -
+
-
+
+
+ + {assignment.questions?.map((question, index) => ( + + ))} + +

@@ -145,115 +107,30 @@ export default function SubmissionPage({ params }: Props) { {DateUtils.parseTimestampToDate(assignment.deadline)}

- {/*

- Number of questions:{" "} - - {assignment.numberOfQuestions} - -

*/} -

-
-
-
-
- {/* Question title */} -
-
- Question 1: Two Sum -
-
- - Given an array of integers nums and an integer target, return - indices of the two numbers such that they add up to target. - You may assume that each input would have exactly one - solution, and you may not use the same element twice. You can - return the answer in any order. -
-
- Example 1: - - nums = [2,7,11,15], target = 9 - - - - Output: [0,1] - - - - Explanation: -

Because nums[0] + nums[1] == 9, we return [0, 1].

-
- - Example 2: - - Input: nums = [3,2,4], target = 6 - - - - Output: [1,2] - - - Constraints: -

{"2 <= nums.length <= 104"}

-

{"-109 <= nums[i] <= 109"}

-

{"-109 <= target <= 109"}

- Only one valid answer exists. -
+ {assignment.questions && ( + + )}
- setValue(value)} - /> + {submission ? ( + + ) : ( + + )}
-
- - {(item) => ( - - - {renderTabContent(item.id, item)} - - - )} - -
{" "} +
diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 119102d3..d3b9f2a0 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -19,21 +19,21 @@ export default function RootLayout({ }>) { return ( - - + +
-
+
{children}
diff --git a/frontend/src/components/common/SideBar.tsx b/frontend/src/components/common/SideBar.tsx index cbe8bd68..15c8a609 100644 --- a/frontend/src/components/common/SideBar.tsx +++ b/frontend/src/components/common/SideBar.tsx @@ -37,11 +37,11 @@ export default function SideBar() { const router = useRouter(); const userName = "Jane Doe"; const userEmail = "janedoe@u.nus.edu"; - const [isCollapsed, setIsCollapsed] = useState(false); + const [isCollapsed, setIsCollapsed] = useState(true); const [isCollapsible, setIsCollapsible] = useState(false); const wrapperClasses = classNames( - "h-screen px-4 pt-8 pb-4 bg-lightgrey text-black flex flex-col", + "h-dvh px-4 pt-8 pb-4 bg-lightgrey text-black flex flex-col", { ["w-60"]: !isCollapsed, ["w-20"]: isCollapsed, diff --git a/frontend/src/components/submission/FeedbackCodeEditor.tsx b/frontend/src/components/submission/FeedbackCodeEditor.tsx new file mode 100644 index 00000000..e091da68 --- /dev/null +++ b/frontend/src/components/submission/FeedbackCodeEditor.tsx @@ -0,0 +1,62 @@ +"use client"; + +import { MutableRefObject, useRef } from "react"; +import Editor from "@monaco-editor/react"; +import * as monaco from "monaco-editor"; + +interface Props { + submission?: Submission; +} + +export default function FeedbackCodeEditor({ submission }: Props) { + const editorRef: MutableRefObject = + useRef(null); + + // const newDecoration: monaco.editor.IModelDeltaDecoration[] = feedback + // ? [ + // { + // range: new monaco.Range(feedback.line, 1, feedback.line, 1), + // options: { + // isWholeLine: true, + // className: "bg-yellow-200", + // }, + // }, + // ] + // : []; + + const newDecoration: monaco.editor.IModelDeltaDecoration[] = + submission && submission.feedbacks + ? submission.feedbacks.map((item) => ({ + range: new monaco.Range(item.line, 1, item.line, 1), + options: { + isWholeLine: true, + className: "bg-yellow-200", + }, + })) + : []; + + const onMount = (editor: monaco.editor.IStandaloneCodeEditor) => { + editorRef.current = editor; + if (submission && submission.feedbacks) { + editor.createDecorationsCollection(newDecoration); + editor.focus(); + } + }; + + return ( +
+ +
+ ); +} diff --git a/frontend/src/components/submission/FeedbackQuestion.tsx b/frontend/src/components/submission/FeedbackQuestion.tsx new file mode 100644 index 00000000..0e63e567 --- /dev/null +++ b/frontend/src/components/submission/FeedbackQuestion.tsx @@ -0,0 +1,26 @@ +import { Divider } from "@nextui-org/react"; +import parse from "html-react-parser"; + +interface Props { + question: Question; +} + +export default function FeedbackQuestion({ question }: Props) { + return ( +
+
+
+
+ {question.title} +
+
+ +
+
+ {parse(question.description)} +
+
+
+
+ ); +} diff --git a/frontend/src/components/submission/FeedbackTabs.tsx b/frontend/src/components/submission/FeedbackTabs.tsx new file mode 100644 index 00000000..6c2e517f --- /dev/null +++ b/frontend/src/components/submission/FeedbackTabs.tsx @@ -0,0 +1,81 @@ +"use client"; + +import { + Tabs, + Tab, + Card, + CardBody, + Spacer, + Divider, + Code, +} from "@nextui-org/react"; + +interface Item { + id: string; + label: string; + content: string[]; +} + +export default function FeedbackTabs() { + const feedback = { + line: 2, + hints: ["Incorrect else block for if ( ((x % 2) == 1) )"], + }; + + const tabs = [ + { + id: "testcases", + label: "Test Cases", + content: [ + "is_odd(1)", + "is_odd(2)", + "is_odd(3)", + "is_odd(0)", + "is_odd(-1)", + ], + }, + { + id: "feedback", + label: "Feedback", + content: [`Line ${feedback.line.toString()}: ${feedback.hints[0]}`], + }, + { + id: "grades", + label: "Grades", + content: [ + "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + ], + }, + ]; + + const renderTabContent = (tabId: string, item: Item) => { + if (tabId === "testcases") { + return ( +
+ {item.content.map((testcase: string) => ( + + {testcase} + + ))} +
+ ); + } else if (tabId === "feedback") { + return item.content[0]; + } else if (tabId === "grades") { + return item.content[0]; + } + }; + return ( +
+ + {(item) => ( + + + {renderTabContent(item.id, item)} + + + )} + +
+ ); +} diff --git a/frontend/src/helpers/grading-service/api-wrapper.ts b/frontend/src/helpers/grading-service/api-wrapper.ts new file mode 100644 index 00000000..5dfa4039 --- /dev/null +++ b/frontend/src/helpers/grading-service/api-wrapper.ts @@ -0,0 +1,59 @@ +import HttpStatusCode from "@/types/HttpStatusCode"; +import axios, { AxiosError } from "axios"; + +const api = axios.create({ + baseURL: process.env.GRADING_API_URL ?? "http://localhost:8080/grading/api", + timeout: 10000, + headers: { + "Content-type": "application/json", + }, +}); + +const getSubmissionByQuestionIdAndStudentId = async ({ + questionId, + studentId, +}: { + questionId: string; + studentId: string; +}) => { + try { + const response = await api.get( + `/questions/${questionId}/submissions?studentId=${studentId}` + ); + + const submission = response.data as Submission; + + // const submission: Submission = { + // id: "clu270b2o000110wo9e73xxja", + // questionId: "cltu7sh7b0007rdzclqfnzbt9", + // studentId: 1, + // code: `def is_odd(x): + // if x % 2 == 1: + // return False + // else: + // return True`, + // language: "python", + // codeParser: `{"importStatements":[],"fncs":{"is_odd":{"name":"is_odd","rettype":"*","initloc":1,"endloc":0,"params":[{"val0":"x","val1":"*","valueArray":["x","*"],"valueList":["x","*"]}],"locexprs":{"1":[{"val0":"$ret","val1":{"name":"ite","args":[{"name":"Eq","args":[{"name":"Mod","args":[{"name":"x","primed":false,"line":2,"tokentype":"Variable"},{"value":"2","line":2,"tokentype":"Constant"}],"line":2,"tokentype":"Operation"},{"value":"1","line":2,"tokentype":"Constant"}],"line":2,"tokentype":"Operation"},{"value":"False","line":3,"tokentype":"Constant"},{"value":"True","line":5,"tokentype":"Constant"}],"line":2,"tokentype":"Operation"},"valueArray":["$ret",{"name":"ite","args":[{"name":"Eq","args":[{"name":"Mod","args":[{"name":"x","primed":false,"line":2,"tokentype":"Variable"},{"value":"2","line":2,"tokentype":"Constant"}],"line":2,"tokentype":"Operation"},{"value":"1","line":2,"tokentype":"Constant"}],"line":2,"tokentype":"Operation"},{"value":"False","line":3,"tokentype":"Constant"},{"value":"True","line":5,"tokentype":"Constant"}],"line":2}],"valueList":["$ret",{"name":"ite","args":[{"name":"Eq","args":[{"name":"Mod","args":[{"name":"x","primed":false,"line":2,"tokentype":"Variable"},{"value":"2","line":2,"tokentype":"Constant"}],"line":2,"tokentype":"Operation"},{"value":"1","line":2,"tokentype":"Constant"}],"line":2,"tokentype":"Operation"},{"value":"False","line":3,"tokentype":"Constant"},{"value":"True","line":5,"tokentype":"Constant"}],"line":2}]}]},"loctrans":{"1":{}},"locdescs":{"1":"around the beginning of function 'is_odd'"},"types":{}}}}`, + // createdOn: Date.now(), + // }; + + return submission; + } catch (error) { + if (axios.isAxiosError(error)) { + switch ((error as AxiosError).response?.status) { + case HttpStatusCode.NOT_FOUND: + return null; + default: + throw new Error("Failed to fetch submission"); + } + } + + throw new Error("Failed to fetch submission"); + } +}; + +const GradingService = { + getSubmissionByQuestionIdAndStudentId, +}; + +export default GradingService; diff --git a/frontend/src/types/grading-service.d.ts b/frontend/src/types/grading-service.d.ts new file mode 100644 index 00000000..7722598e --- /dev/null +++ b/frontend/src/types/grading-service.d.ts @@ -0,0 +1,19 @@ +interface Feedback { + id: string; + submissionId: string; + line: number; + hints: string[]; +} + +interface Submission { + id: string; + questionId: string; + studentId: number; + code: string; + language: string; + feedbacks: { + line: number; + hints: string[]; + }[]; + createdOn: number; +} From 15c80d08fc1da085f7b2b47de10fb11c00921402 Mon Sep 17 00:00:00 2001 From: vivienherq Date: Tue, 9 Apr 2024 12:26:32 +0800 Subject: [PATCH 2/8] Modify returned object of fetch submissions API --- .../[id]/submissions/[sid]/page.tsx | 28 ++++++----------- .../helpers/grading-service/api-wrapper.ts | 30 ++++++------------- 2 files changed, 18 insertions(+), 40 deletions(-) diff --git a/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx b/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx index 36748ce7..bbba16a1 100644 --- a/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx +++ b/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx @@ -19,17 +19,15 @@ interface Props { } export default function SubmissionPage({ params }: Props) { - // const [value, setValue] = useState(""); - // const [language, setLanguage] = useState("python"); - const userId = "1"; - + const userId = 1; const [currentQuestion, setCurrentQuestion] = useState(0); - const [currentQuestionId, setCurrentQuestionId] = useState(""); + const [selectedSubmission, setSelectedSubmission] = useState(0); const handleQuestionChange = (questionNumber: number, questionId: string) => { setCurrentQuestion(questionNumber); setCurrentQuestionId(questionId); + setSelectedSubmission(0); }; const { @@ -40,9 +38,6 @@ export default function SubmissionPage({ params }: Props) { queryKey: ["get-assignment", params.id], queryFn: async () => { const assignment = await AssignmentService.getAssignmentById(params.id); - - console.log(assignment); - return assignment; }, }); @@ -53,16 +48,16 @@ export default function SubmissionPage({ params }: Props) { } }, [assignment]); - const { data: submission, refetch: refetchSubmissions } = useQuery({ + const { data: submissions, refetch: refetchSubmissions } = useQuery({ queryKey: ["get-submissions", params.id, currentQuestionId], queryFn: async () => { - const submission = + const submissions = await GradingService.getSubmissionByQuestionIdAndStudentId({ questionId: currentQuestionId, studentId: userId, }); - return submission; + return submissions; }, }); @@ -74,11 +69,6 @@ export default function SubmissionPage({ params }: Props) { return notFound(); } - const feedback = { - line: 2, - hints: ["Incorrect else block for if ( ((x % 2) == 1) )"], - }; - return (
{assignment && ( @@ -119,10 +109,10 @@ export default function SubmissionPage({ params }: Props) {
- {submission ? ( + {submissions ? ( ) : ( diff --git a/frontend/src/helpers/grading-service/api-wrapper.ts b/frontend/src/helpers/grading-service/api-wrapper.ts index 5dfa4039..91896129 100644 --- a/frontend/src/helpers/grading-service/api-wrapper.ts +++ b/frontend/src/helpers/grading-service/api-wrapper.ts @@ -14,41 +14,29 @@ const getSubmissionByQuestionIdAndStudentId = async ({ studentId, }: { questionId: string; - studentId: string; + studentId: number; }) => { try { const response = await api.get( `/questions/${questionId}/submissions?studentId=${studentId}` ); - const submission = response.data as Submission; - - // const submission: Submission = { - // id: "clu270b2o000110wo9e73xxja", - // questionId: "cltu7sh7b0007rdzclqfnzbt9", - // studentId: 1, - // code: `def is_odd(x): - // if x % 2 == 1: - // return False - // else: - // return True`, - // language: "python", - // codeParser: `{"importStatements":[],"fncs":{"is_odd":{"name":"is_odd","rettype":"*","initloc":1,"endloc":0,"params":[{"val0":"x","val1":"*","valueArray":["x","*"],"valueList":["x","*"]}],"locexprs":{"1":[{"val0":"$ret","val1":{"name":"ite","args":[{"name":"Eq","args":[{"name":"Mod","args":[{"name":"x","primed":false,"line":2,"tokentype":"Variable"},{"value":"2","line":2,"tokentype":"Constant"}],"line":2,"tokentype":"Operation"},{"value":"1","line":2,"tokentype":"Constant"}],"line":2,"tokentype":"Operation"},{"value":"False","line":3,"tokentype":"Constant"},{"value":"True","line":5,"tokentype":"Constant"}],"line":2,"tokentype":"Operation"},"valueArray":["$ret",{"name":"ite","args":[{"name":"Eq","args":[{"name":"Mod","args":[{"name":"x","primed":false,"line":2,"tokentype":"Variable"},{"value":"2","line":2,"tokentype":"Constant"}],"line":2,"tokentype":"Operation"},{"value":"1","line":2,"tokentype":"Constant"}],"line":2,"tokentype":"Operation"},{"value":"False","line":3,"tokentype":"Constant"},{"value":"True","line":5,"tokentype":"Constant"}],"line":2}],"valueList":["$ret",{"name":"ite","args":[{"name":"Eq","args":[{"name":"Mod","args":[{"name":"x","primed":false,"line":2,"tokentype":"Variable"},{"value":"2","line":2,"tokentype":"Constant"}],"line":2,"tokentype":"Operation"},{"value":"1","line":2,"tokentype":"Constant"}],"line":2,"tokentype":"Operation"},{"value":"False","line":3,"tokentype":"Constant"},{"value":"True","line":5,"tokentype":"Constant"}],"line":2}]}]},"loctrans":{"1":{}},"locdescs":{"1":"around the beginning of function 'is_odd'"},"types":{}}}}`, - // createdOn: Date.now(), - // }; - - return submission; + let submissions: Submission[] = response.data; + + submissions.sort((a, b) => b.createdOn - a.createdOn); + + return submissions; } catch (error) { if (axios.isAxiosError(error)) { switch ((error as AxiosError).response?.status) { case HttpStatusCode.NOT_FOUND: - return null; + return []; default: - throw new Error("Failed to fetch submission"); + throw new Error("Failed to fetch submissions"); } } - throw new Error("Failed to fetch submission"); + throw new Error("Failed to fetch submissions"); } }; From 35f5b7103ee5ef6c7f607ebfbcc7db2e97058f16 Mon Sep 17 00:00:00 2001 From: vivienherq Date: Tue, 9 Apr 2024 12:42:39 +0800 Subject: [PATCH 3/8] Update tabs to include feedback --- .../[id]/submissions/[sid]/page.tsx | 6 +- .../components/submission/FeedbackTabs.tsx | 90 ++++++++++--------- 2 files changed, 53 insertions(+), 43 deletions(-) diff --git a/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx b/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx index bbba16a1..41e9f3c3 100644 --- a/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx +++ b/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx @@ -120,7 +120,11 @@ export default function SubmissionPage({ params }: Props) {
- + {submissions ? ( + + ) : ( + + )}
diff --git a/frontend/src/components/submission/FeedbackTabs.tsx b/frontend/src/components/submission/FeedbackTabs.tsx index 6c2e517f..d9a14ae9 100644 --- a/frontend/src/components/submission/FeedbackTabs.tsx +++ b/frontend/src/components/submission/FeedbackTabs.tsx @@ -1,14 +1,6 @@ "use client"; -import { - Tabs, - Tab, - Card, - CardBody, - Spacer, - Divider, - Code, -} from "@nextui-org/react"; +import { Tabs, Tab, Card, CardBody, Code } from "@nextui-org/react"; interface Item { id: string; @@ -16,62 +8,76 @@ interface Item { content: string[]; } -export default function FeedbackTabs() { - const feedback = { - line: 2, - hints: ["Incorrect else block for if ( ((x % 2) == 1) )"], - }; +interface Props { + submission?: Submission; +} + +export default function FeedbackTabs({ submission }: Props) { + const feedback = submission ? submission.feedbacks : []; + const feedbackContent = feedback.map( + (fb) => `Line ${fb.line.toString()}: ${fb.hints[0]}` + ); + const testCases = [ + "is_odd(1)", + "is_odd(2)", + "is_odd(3)", + "is_odd(0)", + "is_odd(-1)", + ]; + const grade = ["5/5"]; const tabs = [ { id: "testcases", label: "Test Cases", - content: [ - "is_odd(1)", - "is_odd(2)", - "is_odd(3)", - "is_odd(0)", - "is_odd(-1)", - ], + content: testCases, }, { id: "feedback", label: "Feedback", - content: [`Line ${feedback.line.toString()}: ${feedback.hints[0]}`], + content: feedbackContent, }, { id: "grades", label: "Grades", - content: [ - "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", - ], + content: grade, }, ]; - const renderTabContent = (tabId: string, item: Item) => { - if (tabId === "testcases") { - return ( -
- {item.content.map((testcase: string) => ( - - {testcase} - - ))} -
- ); - } else if (tabId === "feedback") { - return item.content[0]; - } else if (tabId === "grades") { - return item.content[0]; - } + const renderTabContent = (item: Item) => { + return ( +
+ {item.content.map((testcase: string) => ( + + {testcase} + + ))} +
+ ); + // if (tabId === "testcases") { + // return ( + //
+ // {item.content.map((testcase: string) => ( + // + // {testcase} + // + // ))} + //
+ // ); + // } else if (tabId === "feedback") { + // return item.content[0]; + // } else if (tabId === "grades") { + // return item.content[0]; + // } }; + return (
{(item) => ( - {renderTabContent(item.id, item)} + {renderTabContent(item)} )} From aa8f0b9c64734bf07098e05758c37baab22400f0 Mon Sep 17 00:00:00 2001 From: vivienherq Date: Tue, 9 Apr 2024 13:51:57 +0800 Subject: [PATCH 4/8] Add PostFeedback to API wrapper --- frontend/src/helpers/grading-service/api-wrapper.ts | 13 +++++++++++++ frontend/src/types/grading-service.d.ts | 7 +++++++ 2 files changed, 20 insertions(+) diff --git a/frontend/src/helpers/grading-service/api-wrapper.ts b/frontend/src/helpers/grading-service/api-wrapper.ts index 91896129..91763c2a 100644 --- a/frontend/src/helpers/grading-service/api-wrapper.ts +++ b/frontend/src/helpers/grading-service/api-wrapper.ts @@ -40,8 +40,21 @@ const getSubmissionByQuestionIdAndStudentId = async ({ } }; +const postFeedback = async (requestBody: PostFeedbackBody) => { + try { + const response = await api.post("/feedback/generate", requestBody); + + const postedFeedback = response.data as Feedback; + + return postedFeedback; + } catch (_error) { + throw new Error("Failed to create assignment"); + } +}; + const GradingService = { getSubmissionByQuestionIdAndStudentId, + postFeedback, }; export default GradingService; diff --git a/frontend/src/types/grading-service.d.ts b/frontend/src/types/grading-service.d.ts index 7722598e..b6a6a1b2 100644 --- a/frontend/src/types/grading-service.d.ts +++ b/frontend/src/types/grading-service.d.ts @@ -1,3 +1,10 @@ +interface PostFeedbackBody { + language: string; + sourceCode: string; + questionId: string; + studentId: number; +} + interface Feedback { id: string; submissionId: string; From 93f3dc4c6cfccc77a08edf33e6a3d802058da7ad Mon Sep 17 00:00:00 2001 From: vivienherq Date: Thu, 11 Apr 2024 13:20:15 +0800 Subject: [PATCH 5/8] Modify feedback tabs to take in test cases --- .../components/submission/FeedbackTabs.tsx | 35 +++++-------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/frontend/src/components/submission/FeedbackTabs.tsx b/frontend/src/components/submission/FeedbackTabs.tsx index d9a14ae9..8561ce37 100644 --- a/frontend/src/components/submission/FeedbackTabs.tsx +++ b/frontend/src/components/submission/FeedbackTabs.tsx @@ -10,27 +10,23 @@ interface Item { interface Props { submission?: Submission; + testcases?: TestCase[]; } -export default function FeedbackTabs({ submission }: Props) { +export default function FeedbackTabs({ submission, testcases }: Props) { const feedback = submission ? submission.feedbacks : []; const feedbackContent = feedback.map( (fb) => `Line ${fb.line.toString()}: ${fb.hints[0]}` ); - const testCases = [ - "is_odd(1)", - "is_odd(2)", - "is_odd(3)", - "is_odd(0)", - "is_odd(-1)", - ]; + const testcase = testcases ? testcases : []; + const testcaseContent = testcase.map((tc) => `${tc.input}`); const grade = ["5/5"]; const tabs = [ { id: "testcases", label: "Test Cases", - content: testCases, + content: testcaseContent, }, { id: "feedback", @@ -47,28 +43,13 @@ export default function FeedbackTabs({ submission }: Props) { const renderTabContent = (item: Item) => { return (
- {item.content.map((testcase: string) => ( - - {testcase} + {item.content.map((itemcontent: string) => ( + + {itemcontent} ))}
); - // if (tabId === "testcases") { - // return ( - //
- // {item.content.map((testcase: string) => ( - // - // {testcase} - // - // ))} - //
- // ); - // } else if (tabId === "feedback") { - // return item.content[0]; - // } else if (tabId === "grades") { - // return item.content[0]; - // } }; return ( From 9895ae9d88c8fdd6c93cbb91c5c4e049d682e74b Mon Sep 17 00:00:00 2001 From: vivienherq Date: Thu, 11 Apr 2024 13:20:53 +0800 Subject: [PATCH 6/8] Fetch test cases for each question --- .../app/assignments/[id]/submissions/[sid]/page.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx b/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx index 41e9f3c3..d46b3d67 100644 --- a/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx +++ b/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx @@ -61,8 +61,19 @@ export default function SubmissionPage({ params }: Props) { }, }); + const { data: testCases, refetch: refetchTestCases } = useQuery({ + queryKey: ["get-testcases", params.id, currentQuestionId], + queryFn: async () => { + const testCases = + await AssignmentService.getQuestionTestCases(currentQuestionId); + + return testCases; + }, + }); + useEffect(() => { refetchSubmissions(); + refetchTestCases(); }, [currentQuestionId]); if (isError) { @@ -121,7 +132,7 @@ export default function SubmissionPage({ params }: Props) {
{submissions ? ( - + ) : ( )} From 5f9a7e6e0e487744695905163dafea0f5763ed4c Mon Sep 17 00:00:00 2001 From: vivienherq Date: Thu, 11 Apr 2024 13:46:22 +0800 Subject: [PATCH 7/8] Create submission selection component --- .../[id]/submissions/[sid]/page.tsx | 57 ++++++++++++++++--- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx b/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx index d46b3d67..614de481 100644 --- a/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx +++ b/frontend/src/app/assignments/[id]/submissions/[sid]/page.tsx @@ -1,7 +1,13 @@ "use client"; -import { useEffect, useState } from "react"; -import { Spacer, ButtonGroup, Button } from "@nextui-org/react"; +import { ChangeEvent, SetStateAction, useEffect, useState } from "react"; +import { + Spacer, + ButtonGroup, + Button, + Select, + SelectItem, +} from "@nextui-org/react"; import AssignmentService from "@/helpers/assignment-service/api-wrapper"; import GradingService from "@/helpers/grading-service/api-wrapper"; import { useQuery } from "@tanstack/react-query"; @@ -22,12 +28,15 @@ export default function SubmissionPage({ params }: Props) { const userId = 1; const [currentQuestion, setCurrentQuestion] = useState(0); const [currentQuestionId, setCurrentQuestionId] = useState(""); - const [selectedSubmission, setSelectedSubmission] = useState(0); + const [selectedSubmissionId, setSelectedSubmissionId] = useState(""); const handleQuestionChange = (questionNumber: number, questionId: string) => { setCurrentQuestion(questionNumber); setCurrentQuestionId(questionId); - setSelectedSubmission(0); + }; + + const handleSubmissionSelect = (e: ChangeEvent) => { + setSelectedSubmissionId(e.target.value); }; const { @@ -57,7 +66,11 @@ export default function SubmissionPage({ params }: Props) { studentId: userId, }); - return submissions; + const sortedSubmissions = submissions.sort( + (a, b) => a.createdOn - b.createdOn + ); + + return sortedSubmissions; }, }); @@ -76,6 +89,12 @@ export default function SubmissionPage({ params }: Props) { refetchTestCases(); }, [currentQuestionId]); + useEffect(() => { + if (submissions && submissions.length > 0) { + setSelectedSubmissionId(submissions[0].id); + } + }, [submissions]); + if (isError) { return notFound(); } @@ -119,11 +138,28 @@ export default function SubmissionPage({ params }: Props) { )}
+
+ +
{submissions ? ( submission.id === selectedSubmissionId + )} + key={selectedSubmissionId} /> ) : ( @@ -132,7 +168,12 @@ export default function SubmissionPage({ params }: Props) {
{submissions ? ( - + submission.id === selectedSubmissionId + )} + testcases={testCases} + /> ) : ( )} From 66ac5c3870bfe46a2e5735dd39f1acd40e0ef255 Mon Sep 17 00:00:00 2001 From: vivienherq Date: Thu, 11 Apr 2024 19:33:08 +0800 Subject: [PATCH 8/8] Update packages --- frontend/package.json | 4 +- frontend/yarn.lock | 231 ++++++++++++++++++++++-------------------- 2 files changed, 123 insertions(+), 112 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index ff5bd896..73ce5691 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,7 +19,7 @@ }, "dependencies": { "@monaco-editor/react": "^4.6.0", - "@nextui-org/react": "^2.2.9", + "@nextui-org/react": "^2.2.10", "@radix-ui/react-toast": "^1.1.5", "@tanstack/react-query": "^5.24.1", "axios": "^1.6.7", @@ -28,8 +28,8 @@ "clsx": "^2.1.0", "framer-motion": "^11.0.22", "html-react-parser": "^5.1.8", - "lucide-react": "^0.363.0", "js-cookie": "^3.0.5", + "lucide-react": "^0.363.0", "monaco-editor": "^0.47.0", "next": "14.1.0", "react": "^18", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 6b6ac438..2342b46a 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -301,13 +301,20 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.21.0", "@babel/runtime@^7.24.0": +"@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.21.0": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.1.tgz#431f9a794d173b53720e69a6464abc6f0e2a5c57" integrity sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ== dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.24.0": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd" + integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.22.15", "@babel/template@^7.24.0", "@babel/template@^7.3.3": version "7.24.0" resolved "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz" @@ -838,21 +845,21 @@ "@react-types/overlays" "^3.8.3" "@react-types/shared" "^3.21.0" -"@nextui-org/autocomplete@2.0.9": - version "2.0.9" - resolved "https://registry.npmjs.org/@nextui-org/autocomplete/-/autocomplete-2.0.9.tgz" - integrity sha512-ViPXrZnP35k7LF+TBA4w8nqu0OEj9p1z9Rt7rwrACmY2VmDGY6h6a6nDCMjhuTVXptftRvzxfIPsIyzBYqxb0g== +"@nextui-org/autocomplete@2.0.10": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@nextui-org/autocomplete/-/autocomplete-2.0.10.tgz#8540eef487c14505295a930f1b4b037b45c2aaa8" + integrity sha512-nQr8VC5RtpjnPef1qXgjNxRAw8JbN6q5qIFtsHWOCzvvn5jGAtdxkAkNE4C7DTvlMWZkIlEuR4DyAmFfY8CChQ== dependencies: "@nextui-org/aria-utils" "2.0.15" - "@nextui-org/button" "2.0.26" - "@nextui-org/input" "2.1.16" + "@nextui-org/button" "2.0.27" + "@nextui-org/input" "2.1.17" "@nextui-org/listbox" "2.1.16" - "@nextui-org/popover" "2.1.14" + "@nextui-org/popover" "2.1.15" "@nextui-org/react-utils" "2.0.10" - "@nextui-org/scroll-shadow" "2.1.12" + "@nextui-org/scroll-shadow" "2.1.13" "@nextui-org/shared-icons" "2.0.6" "@nextui-org/shared-utils" "2.0.4" - "@nextui-org/spinner" "2.0.24" + "@nextui-org/spinner" "2.0.25" "@nextui-org/use-aria-button" "2.0.6" "@react-aria/combobox" "^3.7.1" "@react-aria/focus" "^3.14.3" @@ -899,15 +906,15 @@ "@react-types/breadcrumbs" "^3.7.1" "@react-types/shared" "^3.21.0" -"@nextui-org/button@2.0.26": - version "2.0.26" - resolved "https://registry.npmjs.org/@nextui-org/button/-/button-2.0.26.tgz" - integrity sha512-mDrSII1oneY4omwDdxUhl5oLa3AhoWCchwV/jt7egunnAFie32HbTqfFYGpLGiJw3JMMh3WDUthrI1islVTRKA== +"@nextui-org/button@2.0.27": + version "2.0.27" + resolved "https://registry.yarnpkg.com/@nextui-org/button/-/button-2.0.27.tgz#55e221b2ed1f2c436281b65ee919fe886e01ae1c" + integrity sha512-oErzUr9KtE/qjUx4dSbalphxURssxGf9tv0mW++ZMkmVX1E6i887FwZb9xAVm9oBwYwR6+xpJaqjQLmt8aN/rQ== dependencies: "@nextui-org/react-utils" "2.0.10" "@nextui-org/ripple" "2.0.24" "@nextui-org/shared-utils" "2.0.4" - "@nextui-org/spinner" "2.0.24" + "@nextui-org/spinner" "2.0.25" "@nextui-org/use-aria-button" "2.0.6" "@react-aria/button" "^3.8.4" "@react-aria/focus" "^3.14.3" @@ -982,13 +989,13 @@ "@nextui-org/system-rsc" "2.0.11" "@react-types/shared" "^3.21.0" -"@nextui-org/dropdown@2.1.16": - version "2.1.16" - resolved "https://registry.npmjs.org/@nextui-org/dropdown/-/dropdown-2.1.16.tgz" - integrity sha512-3KINNvC7Cz+deQltCM8gaB7iJCfU4Qsp1fwnoy1wUEjeZhEtPOPR59oTyqT+gPaPIisP1+LLOfcqRl4jNQoVXw== +"@nextui-org/dropdown@2.1.17": + version "2.1.17" + resolved "https://registry.yarnpkg.com/@nextui-org/dropdown/-/dropdown-2.1.17.tgz#aa695a40be042609a3e85510315fb620d5d3bae9" + integrity sha512-Hxmz1Yf/LjjOLqWRF49Q5ZYJtae6ydDEk1mv8oMKNmSWHi92lrgmHlwkGvR3mjczbRuF+WkXHLEhVZH6/tZQ7A== dependencies: "@nextui-org/menu" "2.0.17" - "@nextui-org/popover" "2.1.14" + "@nextui-org/popover" "2.1.15" "@nextui-org/react-utils" "2.0.10" "@nextui-org/shared-utils" "2.0.4" "@react-aria/focus" "^3.14.3" @@ -1014,10 +1021,10 @@ "@nextui-org/shared-utils" "2.0.4" "@nextui-org/use-image" "2.0.4" -"@nextui-org/input@2.1.16": - version "2.1.16" - resolved "https://registry.npmjs.org/@nextui-org/input/-/input-2.1.16.tgz" - integrity sha512-nUTlAvsXj5t88ycvQdICxf78/pko6Wznx2OomvYjb3E45eb77twQcWUDhydkJCWIh3b4AhGHSMM6GYxwWUgMDA== +"@nextui-org/input@2.1.17": + version "2.1.17" + resolved "https://registry.yarnpkg.com/@nextui-org/input/-/input-2.1.17.tgz#12c148af654decad60b22f3441b33e79881d3842" + integrity sha512-3FW3NDDbQOa5IlUCpO2Ma/XEjGnx4TQLM8MvMbskc+GNbZ0mtzfV0hCeQkqxxJ2lP4Mkp4QhwGRRkRrDu1G0Wg== dependencies: "@nextui-org/react-utils" "2.0.10" "@nextui-org/shared-icons" "2.0.6" @@ -1094,10 +1101,10 @@ "@react-types/menu" "^3.9.5" "@react-types/shared" "^3.21.0" -"@nextui-org/modal@2.0.28": - version "2.0.28" - resolved "https://registry.npmjs.org/@nextui-org/modal/-/modal-2.0.28.tgz" - integrity sha512-unfP0EMF3FDg5CkRqou03s4/BopWbaBTeVIMZeA2A1WF5teHUOmpLdp44Z1KOoWB1RVMDVd4JeoauNHNhJMp0g== +"@nextui-org/modal@2.0.29": + version "2.0.29" + resolved "https://registry.yarnpkg.com/@nextui-org/modal/-/modal-2.0.29.tgz#a71620bcc1f9d7bd37ed3d54e36e3e5270e5b8db" + integrity sha512-C/pvw0fAPWKbfMoGfIVZWhMRbe+DRGEg7GqPVY7EmW4FSSIK7Sfdn6Jxm+sSv+a7xHpDr86nirFbvN3S4jCaHw== dependencies: "@nextui-org/framer-transitions" "2.0.15" "@nextui-org/react-utils" "2.0.10" @@ -1133,32 +1140,34 @@ "@react-stately/utils" "^3.8.0" react-remove-scroll "^2.5.6" -"@nextui-org/pagination@2.0.26": - version "2.0.26" - resolved "https://registry.npmjs.org/@nextui-org/pagination/-/pagination-2.0.26.tgz" - integrity sha512-OVpkpXqUKRuMRIcYESBAL95d3pqZ17SKAyNINMiJ/DwWnrzJu/LXGmFwTuYRoBdqHFlm7guGqZbHmAkcS/Fgow== +"@nextui-org/pagination@2.0.27": + version "2.0.27" + resolved "https://registry.yarnpkg.com/@nextui-org/pagination/-/pagination-2.0.27.tgz#9d7eb4f397d2e7c604b85b0b946ab3785d653d73" + integrity sha512-v1tSsb0Q863/gKVUxuN7FcE1TZWuvcbWZOrWjKe0/llRgfZ23/4KD1AmFyYuKo5RDFt+i1JWSfzAu08j0Hzzqg== dependencies: "@nextui-org/react-utils" "2.0.10" "@nextui-org/shared-icons" "2.0.6" "@nextui-org/shared-utils" "2.0.4" "@nextui-org/use-aria-press" "2.0.1" - "@nextui-org/use-pagination" "2.0.4" + "@nextui-org/use-pagination" "2.0.5" "@react-aria/focus" "^3.14.3" + "@react-aria/i18n" "^3.8.4" "@react-aria/interactions" "^3.19.1" "@react-aria/utils" "^3.21.1" scroll-into-view-if-needed "3.0.10" -"@nextui-org/popover@2.1.14": - version "2.1.14" - resolved "https://registry.npmjs.org/@nextui-org/popover/-/popover-2.1.14.tgz" - integrity sha512-fqqktFQ/chIBS9Y3MghL6KX6qAy3hodtXUDchnxLa1GL+oi6TCBLUjo+wgI5EMJrTTbqo/eFLui/Ks00JfCj+A== +"@nextui-org/popover@2.1.15": + version "2.1.15" + resolved "https://registry.yarnpkg.com/@nextui-org/popover/-/popover-2.1.15.tgz#3cef5b4edc21251a50ab235572162fb0554953a6" + integrity sha512-FQ66y49sQvXvyDrEsEFAC0qfpl2X+5ZPGaVXdNd3Cjox/jxAxp93cSUkk0iOfYvdsbO5zVFjuM0L3Dqn4hsHMw== dependencies: "@nextui-org/aria-utils" "2.0.15" - "@nextui-org/button" "2.0.26" + "@nextui-org/button" "2.0.27" "@nextui-org/framer-transitions" "2.0.15" "@nextui-org/react-utils" "2.0.10" "@nextui-org/shared-utils" "2.0.4" "@nextui-org/use-aria-button" "2.0.6" + "@nextui-org/use-safe-layout-effect" "2.0.4" "@react-aria/dialog" "^3.5.7" "@react-aria/focus" "^3.14.3" "@react-aria/interactions" "^3.19.1" @@ -1169,10 +1178,10 @@ "@react-types/overlays" "^3.8.3" react-remove-scroll "^2.5.6" -"@nextui-org/progress@2.0.24": - version "2.0.24" - resolved "https://registry.npmjs.org/@nextui-org/progress/-/progress-2.0.24.tgz" - integrity sha512-RPVsFCF8COFClS/8PqEepzryhDFtIcJGQLu/P+qAr7jIDlXizXaBDrp0X34GVtQsapNeE9ExxX9Kt+QIspuHHQ== +"@nextui-org/progress@2.0.25": + version "2.0.25" + resolved "https://registry.yarnpkg.com/@nextui-org/progress/-/progress-2.0.25.tgz#612eca688afa20c06ef9cf900db27a858eb523d8" + integrity sha512-EFVxwT0CXq+2scPLhKKRHkWb6xNa6Vjx+HdgSg3l4lgAxAUryvdfksjW8vjxn6x4I2rGbdzAYPEu27p2KaK7jg== dependencies: "@nextui-org/react-utils" "2.0.10" "@nextui-org/shared-utils" "2.0.4" @@ -1212,49 +1221,49 @@ "@nextui-org/react-rsc-utils" "2.0.10" "@nextui-org/shared-utils" "2.0.4" -"@nextui-org/react@^2.2.9": - version "2.2.9" - resolved "https://registry.npmjs.org/@nextui-org/react/-/react-2.2.9.tgz" - integrity sha512-QHkUQTxI9sYoVjrvTpYm5K68pMDRqD13+DVzdsrkJuETGhbvE2c2CCGc4on9EwXC3JsOxuP/OyqaAmOIuHhYkA== +"@nextui-org/react@^2.2.10": + version "2.2.10" + resolved "https://registry.yarnpkg.com/@nextui-org/react/-/react-2.2.10.tgz#acce053a43451a36d73f467ffbc76dde8bc5691b" + integrity sha512-YJhUIeLnO/FGDbZgfeWEz32RBrH2YFA1qsJQtMF7mza8rjspX/CkankvI7xs1o6sW/TYLSTq7sOF9RGMxLTIAA== dependencies: "@nextui-org/accordion" "2.0.28" - "@nextui-org/autocomplete" "2.0.9" + "@nextui-org/autocomplete" "2.0.10" "@nextui-org/avatar" "2.0.24" "@nextui-org/badge" "2.0.24" "@nextui-org/breadcrumbs" "2.0.4" - "@nextui-org/button" "2.0.26" + "@nextui-org/button" "2.0.27" "@nextui-org/card" "2.0.24" "@nextui-org/checkbox" "2.0.25" "@nextui-org/chip" "2.0.25" "@nextui-org/code" "2.0.24" "@nextui-org/divider" "2.0.25" - "@nextui-org/dropdown" "2.1.16" + "@nextui-org/dropdown" "2.1.17" "@nextui-org/image" "2.0.24" - "@nextui-org/input" "2.1.16" + "@nextui-org/input" "2.1.17" "@nextui-org/kbd" "2.0.25" "@nextui-org/link" "2.0.26" "@nextui-org/listbox" "2.1.16" "@nextui-org/menu" "2.0.17" - "@nextui-org/modal" "2.0.28" + "@nextui-org/modal" "2.0.29" "@nextui-org/navbar" "2.0.27" - "@nextui-org/pagination" "2.0.26" - "@nextui-org/popover" "2.1.14" - "@nextui-org/progress" "2.0.24" + "@nextui-org/pagination" "2.0.27" + "@nextui-org/popover" "2.1.15" + "@nextui-org/progress" "2.0.25" "@nextui-org/radio" "2.0.25" "@nextui-org/ripple" "2.0.24" - "@nextui-org/scroll-shadow" "2.1.12" - "@nextui-org/select" "2.1.20" + "@nextui-org/scroll-shadow" "2.1.13" + "@nextui-org/select" "2.1.21" "@nextui-org/skeleton" "2.0.24" - "@nextui-org/slider" "2.2.5" - "@nextui-org/snippet" "2.0.30" + "@nextui-org/slider" "2.2.6" + "@nextui-org/snippet" "2.0.31" "@nextui-org/spacer" "2.0.24" - "@nextui-org/spinner" "2.0.24" + "@nextui-org/spinner" "2.0.25" "@nextui-org/switch" "2.0.25" "@nextui-org/system" "2.0.15" "@nextui-org/table" "2.0.28" "@nextui-org/tabs" "2.0.26" - "@nextui-org/theme" "2.1.17" - "@nextui-org/tooltip" "2.0.29" + "@nextui-org/theme" "2.1.18" + "@nextui-org/tooltip" "2.0.30" "@nextui-org/user" "2.0.25" "@react-aria/visually-hidden" "^3.8.6" @@ -1266,30 +1275,30 @@ "@nextui-org/react-utils" "2.0.10" "@nextui-org/shared-utils" "2.0.4" -"@nextui-org/scroll-shadow@2.1.12": - version "2.1.12" - resolved "https://registry.npmjs.org/@nextui-org/scroll-shadow/-/scroll-shadow-2.1.12.tgz" - integrity sha512-uxT8D+WCWeBy4xaFDfqVpBgjjHZUwydXsX5HhbzZCBir/1eRG5GMnUES3w98DSwcUVadG64gAVsyGW4HmSZw1Q== +"@nextui-org/scroll-shadow@2.1.13": + version "2.1.13" + resolved "https://registry.yarnpkg.com/@nextui-org/scroll-shadow/-/scroll-shadow-2.1.13.tgz#d4c816dce9bc539cdb4b21b3ecc3ef6770ffb29e" + integrity sha512-hFoVGplGMWuE+KXRz9gtKRq3e0YYkxutrqjDD0BiDHk4WkiyOrTnNuE6wnJTnd6Hd+kavLPBDu2+yGauDb7/Qg== dependencies: "@nextui-org/react-utils" "2.0.10" "@nextui-org/shared-utils" "2.0.4" - "@nextui-org/use-data-scroll-overflow" "2.1.2" + "@nextui-org/use-data-scroll-overflow" "2.1.3" -"@nextui-org/select@2.1.20": - version "2.1.20" - resolved "https://registry.npmjs.org/@nextui-org/select/-/select-2.1.20.tgz" - integrity sha512-GCO9uzyYnFIdJTqIe6aDe2NnYlclcdYfZnECFAze/R2MW0jpoysk5ysGBDjVDmZis6tLu+BOFXJbIlYEi+LoUQ== +"@nextui-org/select@2.1.21": + version "2.1.21" + resolved "https://registry.yarnpkg.com/@nextui-org/select/-/select-2.1.21.tgz#44f3715098a2b503300f80efc89bed3ef36c94ea" + integrity sha512-BVfmxIsZTL6dBiZ1Q5RbAnqiNpVnaJgWi0M1QMV448FHMaDHLTWtNOJPMD0QyxHRNPfDgFrqEAq6a1+pA26ckQ== dependencies: "@nextui-org/aria-utils" "2.0.15" "@nextui-org/listbox" "2.1.16" - "@nextui-org/popover" "2.1.14" + "@nextui-org/popover" "2.1.15" "@nextui-org/react-utils" "2.0.10" - "@nextui-org/scroll-shadow" "2.1.12" + "@nextui-org/scroll-shadow" "2.1.13" "@nextui-org/shared-icons" "2.0.6" "@nextui-org/shared-utils" "2.0.4" - "@nextui-org/spinner" "2.0.24" + "@nextui-org/spinner" "2.0.25" "@nextui-org/use-aria-button" "2.0.6" - "@nextui-org/use-aria-multiselect" "2.1.3" + "@nextui-org/use-aria-multiselect" "2.1.4" "@react-aria/focus" "^3.14.3" "@react-aria/interactions" "^3.19.1" "@react-aria/utils" "^3.21.1" @@ -1315,14 +1324,14 @@ "@nextui-org/shared-utils" "2.0.4" "@nextui-org/system-rsc" "2.0.11" -"@nextui-org/slider@2.2.5": - version "2.2.5" - resolved "https://registry.npmjs.org/@nextui-org/slider/-/slider-2.2.5.tgz" - integrity sha512-dC6HHMmtn2WvxDmbY/Dq51XJjQ7cAnjZsuYVIvhwIiCLDG8QnEIhmYN0DQp/6oeZsCHnyMHC4DmtgOiJL0eXrQ== +"@nextui-org/slider@2.2.6": + version "2.2.6" + resolved "https://registry.yarnpkg.com/@nextui-org/slider/-/slider-2.2.6.tgz#b1c766e78f71840364a43cd1f6f1646196a9b723" + integrity sha512-adCjQ8k4bUwWcvmOJUki3+UVsCz4ms+qLG4jnY2wClPdQAwISMbZzQsuv3km+1HIZE5Ja7jzeeT/dMd8l3n+bg== dependencies: "@nextui-org/react-utils" "2.0.10" "@nextui-org/shared-utils" "2.0.4" - "@nextui-org/tooltip" "2.0.29" + "@nextui-org/tooltip" "2.0.30" "@nextui-org/use-aria-press" "2.0.1" "@react-aria/focus" "^3.14.3" "@react-aria/i18n" "^3.8.4" @@ -1332,16 +1341,16 @@ "@react-aria/visually-hidden" "^3.8.6" "@react-stately/slider" "^3.4.4" -"@nextui-org/snippet@2.0.30": - version "2.0.30" - resolved "https://registry.npmjs.org/@nextui-org/snippet/-/snippet-2.0.30.tgz" - integrity sha512-8hKxqKpbJIMqFVedzYj90T4td+TkWdOdyYD9+VjywMdezAjsWdr8tqQj7boaMFjVNVSG+Pnw55Pgg/vkpc21aw== +"@nextui-org/snippet@2.0.31": + version "2.0.31" + resolved "https://registry.yarnpkg.com/@nextui-org/snippet/-/snippet-2.0.31.tgz#0ac0282b078fa47ec92ae07bdee748fdc2129453" + integrity sha512-WooH5cqlHoa6SqUhzseKY7g1ah8kzSv382u95Or9kIgSirEZCrjygup3nFeKTMAe01NZoAz3OOYO7XNFWJ57vA== dependencies: - "@nextui-org/button" "2.0.26" + "@nextui-org/button" "2.0.27" "@nextui-org/react-utils" "2.0.10" "@nextui-org/shared-icons" "2.0.6" "@nextui-org/shared-utils" "2.0.4" - "@nextui-org/tooltip" "2.0.29" + "@nextui-org/tooltip" "2.0.30" "@nextui-org/use-clipboard" "2.0.4" "@react-aria/focus" "^3.14.3" "@react-aria/utils" "^3.21.1" @@ -1355,10 +1364,10 @@ "@nextui-org/shared-utils" "2.0.4" "@nextui-org/system-rsc" "2.0.11" -"@nextui-org/spinner@2.0.24": - version "2.0.24" - resolved "https://registry.npmjs.org/@nextui-org/spinner/-/spinner-2.0.24.tgz" - integrity sha512-s/q2FmxGPNEqA0ifWfc7xgs5a5D9c3xKkxL3n7jDoRnWo0NPlRsa6QRJGiSL5dHNoUqspRf/lNw2V94Bxk86Pg== +"@nextui-org/spinner@2.0.25": + version "2.0.25" + resolved "https://registry.yarnpkg.com/@nextui-org/spinner/-/spinner-2.0.25.tgz#0a28341adade33c05fc990d6eb3a474b0ada0a25" + integrity sha512-s2iqaB71sanRxglJtG4UZF+Rz/W6UxnYegbkhnkkljH20vhOcrhwm5jKGStq8jkata8UZ0ajS67H8KY8lHV8nw== dependencies: "@nextui-org/react-utils" "2.0.10" "@nextui-org/shared-utils" "2.0.4" @@ -1438,10 +1447,10 @@ "@react-types/tabs" "^3.3.3" scroll-into-view-if-needed "3.0.10" -"@nextui-org/theme@2.1.17": - version "2.1.17" - resolved "https://registry.npmjs.org/@nextui-org/theme/-/theme-2.1.17.tgz" - integrity sha512-/WeHcMrAcWPGsEVn9M9TnvxKkaYkCocBH9JrDYCEFQoJgleUzHd4nVk7MWtpSOYJXLUzUMY1M9AqAK3jBkw+5g== +"@nextui-org/theme@2.1.18": + version "2.1.18" + resolved "https://registry.yarnpkg.com/@nextui-org/theme/-/theme-2.1.18.tgz#32862cde091712710e8ec4877f0aea0c85f2bf14" + integrity sha512-2ptDh350lVD0yejZTpGv4fkeoGKB8+B/Coblzpjijfofn/t6MQIRIRRLp04wCCa/IbeevjS2wyadWpMDtVh3CQ== dependencies: color "^4.2.3" color2k "^2.0.2" @@ -1454,15 +1463,16 @@ lodash.omit "^4.5.0" tailwind-variants "^0.1.18" -"@nextui-org/tooltip@2.0.29": - version "2.0.29" - resolved "https://registry.npmjs.org/@nextui-org/tooltip/-/tooltip-2.0.29.tgz" - integrity sha512-LaFyS5bXhcZFXP9rnh6pTKsYX6siWjzEe5z72FIOyAV2yvv2yhkRiO/mEHKI8moo+/tScW/6muFXsvbEalPefg== +"@nextui-org/tooltip@2.0.30": + version "2.0.30" + resolved "https://registry.yarnpkg.com/@nextui-org/tooltip/-/tooltip-2.0.30.tgz#79e67915d54a64b0b4abe1f10d6dc783f3afb41d" + integrity sha512-V3N9o/oNU1Y11etiilrlqt5dF4/o9eJSttgN2CPo8eRAPc96+sRpdGPGX3XcLJZNFRcNx8BkD/bcEUcrDdjmRA== dependencies: "@nextui-org/aria-utils" "2.0.15" "@nextui-org/framer-transitions" "2.0.15" "@nextui-org/react-utils" "2.0.10" "@nextui-org/shared-utils" "2.0.4" + "@nextui-org/use-safe-layout-effect" "2.0.4" "@react-aria/interactions" "^3.19.1" "@react-aria/overlays" "^3.18.1" "@react-aria/tooltip" "^3.6.4" @@ -1518,10 +1528,10 @@ "@react-stately/overlays" "^3.6.3" "@react-types/shared" "^3.21.0" -"@nextui-org/use-aria-multiselect@2.1.3": - version "2.1.3" - resolved "https://registry.npmjs.org/@nextui-org/use-aria-multiselect/-/use-aria-multiselect-2.1.3.tgz" - integrity sha512-OM1lj2jdl0Q2Zme/ds6qyT4IIGsBJSGNjvkM6pEnpdyoej/HwTKsSEpEFTDGJ5t9J9DWWCEt3hz0uJxOPnZ66Q== +"@nextui-org/use-aria-multiselect@2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@nextui-org/use-aria-multiselect/-/use-aria-multiselect-2.1.4.tgz#e80292b8d9b037881f5ac84950c5544924473695" + integrity sha512-F95sF4eY5TLkom5tIMb+eoT4i0Cc4qygnQRqIosg8OryDbH62/MV4x88GjQsgDCY8dNeWCNVodHXxaWmVSAgyQ== dependencies: "@react-aria/i18n" "^3.8.4" "@react-aria/interactions" "^3.19.1" @@ -1570,10 +1580,10 @@ resolved "https://registry.npmjs.org/@nextui-org/use-clipboard/-/use-clipboard-2.0.4.tgz" integrity sha512-rMcaX0QsolOJ1BQbp1T/FVsSPn2m0Ss4Z+bbdS7eM6EFKtJdVJWlpbrST0/kR2UcW1KWeK27NYmtNPF5+hgZMA== -"@nextui-org/use-data-scroll-overflow@2.1.2": - version "2.1.2" - resolved "https://registry.npmjs.org/@nextui-org/use-data-scroll-overflow/-/use-data-scroll-overflow-2.1.2.tgz" - integrity sha512-3h9QX+dWkfqnqciQc2KeeR67e77hobjefNHGBTDuB4LhJSJ180ToZH09SQNHaUmKRLTU/RABjGWXxdbORI0r6g== +"@nextui-org/use-data-scroll-overflow@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@nextui-org/use-data-scroll-overflow/-/use-data-scroll-overflow-2.1.3.tgz#3ce19c2c5a9da54edbd29d19fabb81d1cfe4fc9b" + integrity sha512-f4rDr4MHGQTyqTd6L4MpKAcKfPDiVeWfYXXXX6gdN8UVTk+PzW675Fe+l7ATBgmaVTn1AEPJwW9dDUJcDpn21g== dependencies: "@nextui-org/shared-utils" "2.0.4" @@ -1605,12 +1615,13 @@ resolved "https://registry.npmjs.org/@nextui-org/use-is-mounted/-/use-is-mounted-2.0.4.tgz" integrity sha512-NSQwQjg8+k02GVov9cDwtAdop1Cr90eDgB0MAdvu7QCMgfBZjy88IdQnx3Yo7bG4wP45xC0vLjqDBanaK+11hw== -"@nextui-org/use-pagination@2.0.4": - version "2.0.4" - resolved "https://registry.npmjs.org/@nextui-org/use-pagination/-/use-pagination-2.0.4.tgz" - integrity sha512-EETHzhh+LW8u2bm93LkUABbu0pIoWBCeY8hmvgjhhNMkILuwZNGYnp9tdF2rcS2P4KDlHQkIQcoiOGrGMqBUaQ== +"@nextui-org/use-pagination@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nextui-org/use-pagination/-/use-pagination-2.0.5.tgz#80a0213834ffa5d420cc28785dc4cea0ad474ab8" + integrity sha512-wH0sC85XeTPPE4zRq0ycAVB+SpmPEiSmTEGxpBG2sqiJlsrNfEeXvTKf73INXM4IWfP53ONAQ7Nd1T7EVuYSkw== dependencies: "@nextui-org/shared-utils" "2.0.4" + "@react-aria/i18n" "^3.8.4" "@nextui-org/use-safe-layout-effect@2.0.4": version "2.0.4" @@ -7208,7 +7219,7 @@ regenerator-runtime@^0.12.0: regenerator-runtime@^0.14.0: version "0.14.1" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2: