99 SolutionState ,
1010 SolutionActualityDto ,
1111 SolutionActualityPart , StudentDataDto
12- } from '../.. /api'
12+ } from '@ /api'
1313import ApiSingleton from "../../api/ApiSingleton" ;
1414import { Alert , Avatar , Rating , Stack , Tooltip , Card , CardContent , CardActions , IconButton , Chip } from "@mui/material" ;
1515import AvatarUtils from "../Utils/AvatarUtils" ;
@@ -24,6 +24,8 @@ import CloseIcon from '@mui/icons-material/Close';
2424import { useSnackbar } from 'notistack' ;
2525import StudentStatsUtils from "../../services/StudentStatsUtils" ;
2626import { StudentCharacteristics } from "@/components/Students/StudentCharacteristics" ;
27+ import KeyboardCommandKeyIcon from '@mui/icons-material/KeyboardCommandKey' ;
28+ import MouseOutlinedIcon from '@mui/icons-material/MouseOutlined' ;
2729
2830interface ISolutionProps {
2931 courseId : number ,
@@ -70,6 +72,34 @@ const TaskSolutionComponent: FC<ISolutionProps> = (props) => {
7072 getActuality ( )
7173 } , [ props . student . userId , props . task . id , props . solution ?. id , props . solution ?. rating ] )
7274
75+ const [ isCtrlPressed , setIsCtrlPressed ] = useState ( false )
76+
77+ useEffect ( ( ) => {
78+ if ( ! props . forMentor ) return
79+
80+ const handleKeyDown = ( event : KeyboardEvent ) => {
81+ if ( event . key === "Control" ) {
82+ setIsCtrlPressed ( true ) ;
83+ }
84+ }
85+
86+ const handleKeyUp = ( event : KeyboardEvent ) => {
87+ if ( event . key === "Control" ) {
88+ setIsCtrlPressed ( false ) ;
89+ }
90+ }
91+
92+ window . addEventListener ( "keydown" , handleKeyDown ) ;
93+ window . addEventListener ( "keyup" , handleKeyUp ) ;
94+
95+ return ( ) => {
96+ if ( ! props . forMentor ) return
97+ window . removeEventListener ( "keydown" , handleKeyDown ) ;
98+ window . removeEventListener ( "keyup" , handleKeyUp ) ;
99+ }
100+ } , [ ] )
101+
102+
73103 useEffect ( ( ) => {
74104 if ( ! state . clickedForRate ) return
75105 RatingStorage . set ( storageKey , { points : state . points , comment : state . lecturerComment } )
@@ -111,7 +141,7 @@ const TaskSolutionComponent: FC<ISolutionProps> = (props) => {
111141 }
112142 }
113143
114- const rateSolution = async ( ) => {
144+ const rateSolution = async ( points : number , lecturerComment : string ) => {
115145 setRateInProgressState ( true )
116146 if ( props . solution ) {
117147 await ApiSingleton . solutionsApi . solutionsRateSolution (
@@ -126,9 +156,9 @@ const TaskSolutionComponent: FC<ISolutionProps> = (props) => {
126156 {
127157 comment : "" ,
128158 githubUrl : "" ,
129- lecturerComment : state . lecturerComment ,
159+ lecturerComment : lecturerComment ,
130160 publicationDate : undefined ,
131- rating : state . points ,
161+ rating : points ,
132162 studentId : props . student . userId
133163 }
134164 )
@@ -173,15 +203,20 @@ const TaskSolutionComponent: FC<ISolutionProps> = (props) => {
173203 title = { < div > { status . comment } </ div > } > { icon } </ Tooltip >
174204 }
175205
206+ const clickForRate = async ( points : number , clickedForRate : boolean ) => {
207+ setState ( ( prevState ) => ( {
208+ ...prevState ,
209+ points : points ,
210+ clickedForRate : clickedForRate && ! isCtrlPressed
211+ } ) )
212+ if ( isCtrlPressed ) await rateSolution ( points , lecturerComment )
213+ }
214+
176215 const renderRateInput = ( ) => {
177216 const showThumbs = maxRating === 1
178217 const isEditable = props . forMentor && ( ! isRated || state . clickedForRate )
179218 const thumbsHandler = ( rating : number ) => {
180- setState ( ( prevState ) => ( {
181- ...prevState ,
182- points : rating ,
183- clickedForRate : isEditable
184- } ) )
219+ clickForRate ( rating , isEditable )
185220 }
186221 if ( maxRating <= 10 && points <= maxRating && ! addBonusPoints )
187222 return ( < Grid container item direction = { "row" } spacing = { 1 } alignItems = { "center" } >
@@ -204,12 +239,7 @@ const TaskSolutionComponent: FC<ISolutionProps> = (props) => {
204239 value = { points }
205240 readOnly = { ! isEditable }
206241 onChange = { ( _ , newValue ) => {
207- setState ( ( prevState ) => ( {
208- ...prevState ,
209- points : newValue || 0 ,
210- addBonusPoints : points > maxRating ,
211- clickedForRate : true
212- } ) )
242+ clickForRate ( newValue || 0 , true )
213243 } }
214244 />
215245 </ Grid > }
@@ -288,6 +318,19 @@ const TaskSolutionComponent: FC<ISolutionProps> = (props) => {
288318 < Grid item >
289319 { renderRateInput ( ) }
290320 </ Grid >
321+ { ! isRated && ! state . clickedForRate && maxRating <= 10 && ! addBonusPoints && < Grid item >
322+ < Typography variant = { "caption" } style = { { color : "GrayText" } } >
323+ Нажмите{ " " }
324+ < span style = { { color : isCtrlPressed ? "blue" : "inherit" } } >
325+ < KeyboardCommandKeyIcon style = { { fontSize : 10 , marginTop : - 2 } } />
326+ Ctrl
327+ </ span > { " " } + { " " }
328+ < span >
329+ ЛКМ
330+ < MouseOutlinedIcon style = { { fontSize : 10 , marginTop : - 2 } } />
331+ </ span > { " " } для быстрого оценивания
332+ </ Typography >
333+ </ Grid > }
291334 { lastRating !== undefined && state . clickedForRate &&
292335 < Grid item >
293336 < Typography variant = { "caption" } style = { { color : "GrayText" , marginTop : - 10 } } >
@@ -343,7 +386,7 @@ const TaskSolutionComponent: FC<ISolutionProps> = (props) => {
343386 loading = { rateInProgress }
344387 loadingPosition = "end"
345388 size = "small"
346- onClick = { rateSolution }
389+ onClick = { ( ) => rateSolution ( points , lecturerComment ) }
347390 >
348391 { isRated ? "Изменить оценку" : "Оценить решение" }
349392 </ LoadingButton >
0 commit comments