Skip to content

Commit 7029cdc

Browse files
committed
Keep criteria values when editing rating
1 parent a9a66ed commit 7029cdc

1 file changed

Lines changed: 82 additions & 24 deletions

File tree

hwproj.front/src/components/Solutions/TaskSolutionComponent.tsx

Lines changed: 82 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)