@@ -116,16 +116,83 @@ const TaskSolutionComponent: FC<ISolutionProps> = (props) => {
116116 }
117117 } ;
118118
119+ const parseNumber = ( value : string ) : number | undefined => {
120+ const match = value . replace ( "," , "." ) . match ( / - ? \d + ( \. \d + ) ? / ) ;
121+ if ( ! match ) return undefined ;
122+
123+ const parsed = Number ( match [ 0 ] ) ;
124+ return Number . isFinite ( parsed ) ? parsed : undefined ;
125+ } ;
126+
127+ const parseStoredCriteria = ( comment ?: string ) => {
128+ const criteria = new Map < string , number > ( ) ;
129+ let extraScore : number | undefined ;
130+ if ( ! comment ) return { criteria, extraScore, commentWithoutCriteria : "" } ;
131+
132+ const lines = comment . split ( "\n" ) ;
133+ const tableStart = lines . findIndex ( line =>
134+ line . trim ( ) === "| Критерий оценивания | Баллы |"
135+ ) ;
136+ if ( tableStart === - 1 ) {
137+ return { criteria, extraScore, commentWithoutCriteria : comment } ;
138+ }
139+
140+ let tableEnd = tableStart + 1 ;
141+ for ( ; tableEnd < lines . length ; tableEnd ++ ) {
142+ const line = lines [ tableEnd ] . trim ( ) ;
143+ if ( ! line . startsWith ( "|" ) || ! line . endsWith ( "|" ) ) break ;
144+ }
145+
146+ lines . slice ( tableStart + 2 , tableEnd ) . forEach ( line => {
147+ const cells = line
148+ . split ( "|" )
149+ . slice ( 1 , - 1 )
150+ . map ( cell => cell . trim ( ) ) ;
151+ if ( cells . length < 2 ) return ;
152+
153+ const value = parseNumber ( cells [ 1 ] ) ;
154+ if ( value === undefined ) return ;
155+
156+ if ( cells [ 0 ] === "Доп. оценка" ) {
157+ extraScore = value ;
158+ } else {
159+ criteria . set ( cells [ 0 ] , value ) ;
160+ }
161+ } ) ;
162+
163+ const commentWithoutCriteria = [
164+ ...lines . slice ( 0 , tableStart ) ,
165+ ...lines . slice ( tableEnd ) ,
166+ ] . join ( "\n" ) . trim ( ) ;
167+
168+ return { criteria, extraScore, commentWithoutCriteria} ;
169+ } ;
170+
171+ const getInitialCriterionValue = (
172+ criterionId : number ,
173+ criterionName : string ,
174+ draft : CriteriaDraft | null ,
175+ storedCriteria : Map < string , number >
176+ ) => {
177+ const draftValue = draft ?. criteria
178+ ?. find ( x => x . criterionId === criterionId ) ?. value ;
179+ if ( typeof draftValue === "number" ) return draftValue ;
180+
181+ const storedValue = storedCriteria . get ( criterionName ) ;
182+ return storedValue ?? Number . NaN ;
183+ } ;
184+
119185 const getDefaultState = ( ) : ISolutionState => {
120186 const storageValue = RatingStorage . tryGet ( storageKey ) ;
187+ const storedCriteria = parseStoredCriteria ( props . solution ?. lecturerComment ) ;
121188
122189 const clickedForRate = props . forMentor
123- ? ( storageValue !== null )
190+ ? ( storageValue != null )
124191 : false ;
125192
126193 return {
127194 points : storageValue ?. points || props . solution ?. rating || 0 ,
128- lecturerComment : storageValue ?. comment || props . solution ?. lecturerComment || "" ,
195+ lecturerComment : storageValue ?. comment || storedCriteria . commentWithoutCriteria ,
129196 clickedForRate,
130197 addBonusPoints : hasCriteria ,
131198 } ;
@@ -137,6 +204,7 @@ const TaskSolutionComponent: FC<ISolutionProps> = (props) => {
137204 const [ state , setState ] = useState < ISolutionState > ( getDefaultState ) ;
138205
139206 const initialDraft = loadCriteriaDraft ( ) ;
207+ const initialStoredCriteria = parseStoredCriteria ( props . solution ?. lecturerComment ) ;
140208
141209 const getDeadlineCriterionValue = ( criterion : { arguments ?: string ; maxPoints ?: number } ) => {
142210 if ( ! props . solution ?. publicationDate || ! criterion . arguments ) return Number . NaN ;
@@ -155,21 +223,21 @@ const TaskSolutionComponent: FC<ISolutionProps> = (props) => {
155223 const deadlineValue = c . type === CriterionTypeDeadline
156224 ? getDeadlineCriterionValue ( c )
157225 : Number . NaN ;
158- const draftValue = initialDraft ?. criteria
159- ?. find ( x => x . criterionId === id ) ?. value ;
160226
161227 return {
162228 criterionId : id ,
163229 name : c . name ?? "" ,
164230 maxPoints : c . maxPoints ?? 0 ,
165- value : Number . isFinite ( deadlineValue ) ? deadlineValue : ( draftValue ?? NaN ) ,
231+ value : Number . isFinite ( deadlineValue )
232+ ? deadlineValue
233+ : getInitialCriterionValue ( id , c . name ?? "" , initialDraft , initialStoredCriteria . criteria ) ,
166234 comment : "" ,
167235 } ;
168236 } )
169237 ) ;
170238
171239 const [ extraScore , setExtraScore ] = useState < number > (
172- initialDraft ?. extraScore ?? 0
240+ initialDraft ?. extraScore ?? initialStoredCriteria . extraScore ?? 0
173241 ) ;
174242 const [ criteriaModified , setCriteriaModified ] = useState ( false ) ;
175243 const [ showOriginalCommentText , setShowOriginalCommentText ] = useState < boolean > ( false )
@@ -183,33 +251,34 @@ const TaskSolutionComponent: FC<ISolutionProps> = (props) => {
183251 setState ( getDefaultState ( ) ) ;
184252
185253 const draft = loadCriteriaDraft ( ) ;
254+ const storedCriteria = parseStoredCriteria ( props . solution ?. lecturerComment ) ;
186255
187256 setCriterionRatings (
188257 ( taskWithCriteria . criteria ?? [ ] ) . map ( c => {
189258 const id = c . id ?? 0 ;
190259 const deadlineValue = c . type === CriterionTypeDeadline
191260 ? getDeadlineCriterionValue ( c )
192261 : Number . NaN ;
193- const draftValue = draft ?. criteria
194- ?. find ( x => x . criterionId === id ) ?. value ;
195262
196263 return {
197264 criterionId : id ,
198265 name : c . name ?? "" ,
199266 maxPoints : c . maxPoints ?? 0 ,
200- value : Number . isFinite ( deadlineValue ) ? deadlineValue : ( draftValue ?? NaN ) ,
267+ value : Number . isFinite ( deadlineValue )
268+ ? deadlineValue
269+ : getInitialCriterionValue ( id , c . name ?? "" , draft , storedCriteria . criteria ) ,
201270 comment : "" ,
202271 } ;
203272 } )
204273 ) ;
205274
206- setExtraScore ( draft ?. extraScore ?? 0 ) ;
275+ setExtraScore ( draft ?. extraScore ?? storedCriteria . extraScore ?? 0 ) ;
207276 setCriteriaModified ( false ) ;
208277 getAchievementState ( ) ;
209278 setRateInProgressState ( false ) ;
210279 getActuality ( ) ;
211280 setShowOriginalCommentText ( false ) ;
212- } , [ props . student . userId , props . task . id , props . solution ?. id , props . solution ?. rating ] ) ;
281+ } , [ props . student . userId , props . task . id , props . solution ?. id , props . solution ?. rating , props . solution ?. lecturerComment ] ) ;
213282
214283 useEffect ( ( ) => {
215284 if ( ! hasCriteria || ! state . addBonusPoints || ! state . clickedForRate || ! criteriaModified ) return ;
@@ -1072,10 +1141,11 @@ const TaskSolutionComponent: FC<ISolutionProps> = (props) => {
10721141 < Button
10731142 size = "small"
10741143 onClick = { ( ) => {
1144+ const storedCriteria = parseStoredCriteria ( props . solution ?. lecturerComment ) ;
10751145 setState ( prevState => ( {
10761146 ...prevState ,
10771147 points : props . solution ?. rating || 0 ,
1078- lecturerComment : props . solution ?. lecturerComment || "" ,
1148+ lecturerComment : storedCriteria . commentWithoutCriteria ,
10791149 addBonusPoints : hasCriteria ,
10801150 clickedForRate : false ,
10811151 } ) ) ;
@@ -1093,20 +1163,8 @@ const TaskSolutionComponent: FC<ISolutionProps> = (props) => {
10931163 size = "small"
10941164 onClick = { ( ) => {
10951165 if ( hasCriteria ) {
1096- setCriterionRatings ( prev =>
1097- prev . map ( cr => ( {
1098- ...cr ,
1099- value : taskWithCriteria . criteria ?. find ( c => c . id === cr . criterionId ) ?. type === CriterionTypeDeadline
1100- ? getDeadlineCriterionValue ( taskWithCriteria . criteria . find ( c => c . id === cr . criterionId ) ! )
1101- : 0 ,
1102- } ) )
1103- ) ;
1104- setExtraScore ( 0 ) ;
1105- setCriteriaModified ( true ) ;
1106-
11071166 setState ( prev => ( {
11081167 ...prev ,
1109- points : 0 ,
11101168 clickedForRate : true ,
11111169 } ) ) ;
11121170 } else {
0 commit comments