11import { EditableCellFactory } from "@components/ui/EditableTable/EditableCellFactory" ;
22import { TableSelectionOverlay } from "@components/ui/EditableTable/TableOverlay" ;
33import { ExerciseTypeTag } from "@components/ui/ExerciseTypeTag" ;
4- import { useReorderAssignmentExercisesMutation } from "@store/assignmentExercise/assignmentExercise.logic.api" ;
4+ import { useToastContext } from "@components/ui/ToastContext" ;
5+ import {
6+ useHasApiKeyQuery ,
7+ useReorderAssignmentExercisesMutation ,
8+ useUpdateAssignmentQuestionsMutation
9+ } from "@store/assignmentExercise/assignmentExercise.logic.api" ;
10+ import { Button } from "primereact/button" ;
511import { Column } from "primereact/column" ;
612import { DataTable , DataTableSelectionMultipleChangeEvent } from "primereact/datatable" ;
13+ import { Dropdown } from "primereact/dropdown" ;
14+ import { OverlayPanel } from "primereact/overlaypanel" ;
715import { Tooltip } from "primereact/tooltip" ;
816import { useRef , useState } from "react" ;
917
18+ import { useExercisesSelector } from "@/hooks/useExercisesSelector" ;
19+
1020import { difficultyOptions } from "@/config/exerciseTypes" ;
1121import { useJwtUser } from "@/hooks/useJwtUser" ;
22+ import { useSelectedAssignment } from "@/hooks/useSelectedAssignment" ;
1223import { DraggingExerciseColumns } from "@/types/components/editableTableCell" ;
1324import { Exercise , supportedExerciseTypesToEdit } from "@/types/exercises" ;
1425
@@ -19,6 +30,68 @@ import { ExercisePreviewModal } from "../components/ExercisePreview/ExercisePrev
1930
2031import { SetCurrentEditExercise , ViewModeSetter , MouseUpHandler } from "./types" ;
2132
33+ const AsyncModeHeader = ( { hasApiKey } : { hasApiKey : boolean } ) => {
34+ const { showToast } = useToastContext ( ) ;
35+ const [ updateExercises ] = useUpdateAssignmentQuestionsMutation ( ) ;
36+ const { assignmentExercises = [ ] } = useExercisesSelector ( ) ;
37+ const overlayRef = useRef < OverlayPanel > ( null ) ;
38+ const [ value , setValue ] = useState ( "Standard" ) ;
39+
40+ const handleSubmit = async ( ) => {
41+ const exercises = assignmentExercises . map ( ( ex ) => ( {
42+ ...ex ,
43+ question_json : JSON . stringify ( ex . question_json ) ,
44+ use_llm : value === "LLM"
45+ } ) ) ;
46+ const { error } = await updateExercises ( exercises ) ;
47+ if ( ! error ) {
48+ overlayRef . current ?. hide ( ) ;
49+ showToast ( { severity : "success" , summary : "Success" , detail : "Exercises updated successfully" } ) ;
50+ } else {
51+ showToast ( { severity : "error" , summary : "Error" , detail : "Failed to update exercises" } ) ;
52+ }
53+ } ;
54+
55+ return (
56+ < div className = "flex align-items-center gap-2" >
57+ < span > Async Mode</ span >
58+ < Button
59+ className = "icon-button-sm"
60+ tooltip = 'Edit "Async Mode" for all exercises'
61+ rounded
62+ text
63+ severity = "secondary"
64+ size = "small"
65+ icon = "pi pi-pencil"
66+ onClick = { ( e ) => overlayRef . current ?. toggle ( e ) }
67+ />
68+ < OverlayPanel closeIcon ref = { overlayRef } style = { { width : "17rem" } } >
69+ < div className = "p-1 flex gap-2 flex-column align-items-center justify-content-around" >
70+ < div > < span > Edit "Async Mode" for all exercises</ span > </ div >
71+ < div style = { { width : "100%" } } >
72+ < Dropdown
73+ style = { { width : "100%" } }
74+ value = { value }
75+ onChange = { ( e ) => setValue ( e . value ) }
76+ options = { [
77+ { label : "Standard" , value : "Standard" } ,
78+ { label : "LLM" , value : "LLM" , disabled : ! hasApiKey }
79+ ] }
80+ optionLabel = "label"
81+ optionDisabled = "disabled"
82+ scrollHeight = "auto"
83+ />
84+ </ div >
85+ < div className = "flex flex-row justify-content-around align-items-center w-full" >
86+ < Button size = "small" severity = "danger" onClick = { ( ) => overlayRef . current ?. hide ( ) } > Cancel</ Button >
87+ < Button size = "small" onClick = { handleSubmit } > Submit</ Button >
88+ </ div >
89+ </ div >
90+ </ OverlayPanel >
91+ </ div >
92+ ) ;
93+ } ;
94+
2295interface AssignmentExercisesTableProps {
2396 assignmentExercises : Exercise [ ] ;
2497 selectedExercises : Exercise [ ] ;
@@ -48,6 +121,11 @@ export const AssignmentExercisesTable = ({
48121} : AssignmentExercisesTableProps ) => {
49122 const { username } = useJwtUser ( ) ;
50123 const [ reorderExercises ] = useReorderAssignmentExercisesMutation ( ) ;
124+ const [ updateAssignmentQuestions ] = useUpdateAssignmentQuestionsMutation ( ) ;
125+ const { selectedAssignment } = useSelectedAssignment ( ) ;
126+ const { data : { hasApiKey = false , asyncLlmModesEnabled = false } = { } } = useHasApiKeyQuery ( ) ;
127+ const isPeerAsync =
128+ selectedAssignment ?. kind === "Peer" && selectedAssignment ?. peer_async_visible === true ;
51129 const dataTableRef = useRef < DataTable < Exercise [ ] > > ( null ) ;
52130 const [ copyModalVisible , setCopyModalVisible ] = useState ( false ) ;
53131 const [ selectedExerciseForCopy , setSelectedExerciseForCopy ] = useState < Exercise | null > ( null ) ;
@@ -276,6 +354,32 @@ export const AssignmentExercisesTable = ({
276354 />
277355 ) }
278356 />
357+ { isPeerAsync && asyncLlmModesEnabled && (
358+ < Column
359+ resizeable = { false }
360+ style = { { width : "12rem" } }
361+ header = { ( ) => < AsyncModeHeader hasApiKey = { hasApiKey } /> }
362+ bodyStyle = { { padding : 0 } }
363+ body = { ( data : Exercise ) => (
364+ < div className = "editable-table-cell" style = { { position : "relative" } } >
365+ < Dropdown
366+ className = "editable-table-dropdown"
367+ value = { data . use_llm && hasApiKey ? "LLM" : "Standard" }
368+ onChange = { ( e ) => updateAssignmentQuestions ( [ { ...data , question_json : JSON . stringify ( data . question_json ) , use_llm : e . value === "LLM" } ] ) }
369+ options = { [
370+ { label : "Standard" , value : "Standard" } ,
371+ { label : "LLM" , value : "LLM" , disabled : ! hasApiKey }
372+ ] }
373+ optionLabel = "label"
374+ optionDisabled = "disabled"
375+ scrollHeight = "auto"
376+ tooltip = { ! hasApiKey ? "Add an API key to enable LLM mode" : undefined }
377+ tooltipOptions = { { showOnDisabled : true } }
378+ />
379+ </ div >
380+ ) }
381+ />
382+ ) }
279383 < Column resizeable = { false } rowReorder style = { { width : "3rem" } } />
280384 </ DataTable >
281385 < TableSelectionOverlay
0 commit comments