Skip to content

Commit 2bcf684

Browse files
author
Martin Krulis
committed
Interface for handling new "reviewed" flag of solutions. Refactoring "accepted" flag to use new universal set-flag API endpoint.
1 parent 252da4d commit 2bcf684

File tree

18 files changed

+247
-163
lines changed

18 files changed

+247
-163
lines changed

src/components/Assignments/SolutionsTable/SolutionsTableRow.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import Points from './Points';
1010
import EnvironmentsListItem from '../../helpers/EnvironmentsList/EnvironmentsListItem';
1111
import DeleteSolutionButtonContainer from '../../../containers/DeleteSolutionButtonContainer/DeleteSolutionButtonContainer';
1212
import AcceptSolutionContainer from '../../../containers/AcceptSolutionContainer';
13+
import ReviewSolutionContainer from '../../../containers/ReviewSolutionContainer';
1314

1415
import CommentsIcon from './CommentsIcon';
15-
import { SendIcon } from '../../icons';
16+
import Icon, { SendIcon } from '../../icons';
1617
import DateTime from '../../widgets/DateTime';
1718

1819
import withLinks from '../../../helpers/withLinks';
@@ -32,6 +33,7 @@ const SolutionsTableRow = ({
3233
actualPoints,
3334
solution: { createdAt },
3435
accepted = false,
36+
reviewed = false,
3537
isBestSolution = false,
3638
runtimeEnvironment = null,
3739
commentsStats = null,
@@ -68,6 +70,22 @@ const SolutionsTableRow = ({
6870
accepted={accepted}
6971
isBestSolution={isBestSolution}
7072
/>
73+
74+
{reviewed && (
75+
<OverlayTrigger
76+
placement="right"
77+
overlay={
78+
<Tooltip id={`reviewed-${id}`}>
79+
<FormattedMessage
80+
id="app.solutionsTable.reviewedTooltip"
81+
defaultMessage="The solution has been reviewed by the supervisor."
82+
/>
83+
</Tooltip>
84+
}>
85+
<Icon icon="stamp" className="text-muted" gapLeft={!splitOnTwoLines} />
86+
</OverlayTrigger>
87+
)}
88+
7189
<CommentsIcon id={id} commentsStats={commentsStats} gapLeft={!splitOnTwoLines} />
7290
</td>
7391

@@ -122,8 +140,11 @@ const SolutionsTableRow = ({
122140
<FormattedMessage id="generic.detail" defaultMessage="Detail" />
123141
</Link>
124142
)}
125-
{permissionHints && permissionHints.setAccepted && (
126-
<AcceptSolutionContainer id={id} locale={locale} shortLabel bsSize="xs" />
143+
{permissionHints && permissionHints.setFlag && (
144+
<React.Fragment>
145+
<AcceptSolutionContainer id={id} locale={locale} shortLabel bsSize="xs" />
146+
<ReviewSolutionContainer id={id} locale={locale} bsSize="xs" />
147+
</React.Fragment>
127148
)}
128149
{permissionHints && permissionHints.delete && (
129150
<DeleteSolutionButtonContainer id={id} groupId={groupId} bsSize="xs" />
@@ -165,6 +186,7 @@ SolutionsTableRow.propTypes = {
165186
createdAt: PropTypes.number.isRequired,
166187
}),
167188
accepted: PropTypes.bool,
189+
reviewed: PropTypes.bool,
168190
isBestSolution: PropTypes.bool,
169191
commentsStats: PropTypes.object,
170192
runtimeEnvironment: PropTypes.object,

src/components/Solutions/SolutionDetail/SolutionDetail.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class SolutionDetail extends Component {
3939
bonusPoints,
4040
actualPoints,
4141
accepted,
42+
reviewed,
4243
runtimeEnvironmentId,
4344
lastSubmission,
4445
permissionHints = EMPTY_OBJ,
@@ -72,6 +73,7 @@ class SolutionDetail extends Component {
7273
submittedBy={submittedBy}
7374
note={note}
7475
accepted={accepted}
76+
reviewed={reviewed}
7577
assignment={assignment}
7678
actualPoints={actualPoints}
7779
maxPoints={maxPoints}
@@ -222,7 +224,8 @@ SolutionDetail.propTypes = {
222224
bonusPoints: PropTypes.number.isRequired,
223225
overriddenPoints: PropTypes.number,
224226
actualPoints: PropTypes.number,
225-
accepted: PropTypes.bool,
227+
accepted: PropTypes.bool.isRequired,
228+
reviewed: PropTypes.bool.isRequired,
226229
runtimeEnvironmentId: PropTypes.string,
227230
permissionHints: PropTypes.object,
228231
}).isRequired,

src/components/Solutions/SolutionStatus/SolutionStatus.js

Lines changed: 27 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const SolutionStatus = ({
2020
submittedBy,
2121
note,
2222
accepted,
23+
reviewed,
2324
environment,
2425
maxPoints,
2526
bonusPoints,
@@ -125,65 +126,44 @@ const SolutionStatus = ({
125126

126127
<tr>
127128
<td className="text-center">
129+
<AssignmentStatusIcon id={String(submittedAt)} status={evaluationStatus} accepted={accepted} />
130+
</td>
131+
<th className="text-nowrap">
132+
<FormattedMessage id="app.solution.scoredPoints" defaultMessage="Final score" />:
133+
</th>
134+
<td
135+
className={classnames({
136+
'text-danger': actualPoints + bonusPoints <= 0,
137+
'text-success': actualPoints + bonusPoints > 0,
138+
})}>
128139
<b>
129-
<AssignmentStatusIcon id={String(submittedAt)} status={evaluationStatus} accepted={accepted} />
140+
{actualPoints || 0}
141+
{bonusPoints !== 0 ? (bonusPoints >= 0 ? '+' : '') + bonusPoints : ''} / {maxPoints}
130142
</b>
131143
</td>
144+
</tr>
145+
146+
<tr>
147+
<td className="text-center">
148+
<Icon icon="check-circle" />
149+
</td>
132150
<th className="text-nowrap">
133-
<FormattedMessage id="app.solution.lastEvaluationStatus" defaultMessage="Last evaluation status" />:
151+
<FormattedMessage id="app.solution.acceptedAsFinal" defaultMessage="Accepted as final" />:
134152
</th>
135153
<td>
136-
<em>
137-
{evaluationStatus === 'done' && (
138-
<FormattedMessage
139-
id="app.submission.evaluation.status.isCorrect"
140-
defaultMessage="The solution is correct and meets all criteria."
141-
/>
142-
)}
143-
{evaluationStatus === 'work-in-progress' && (
144-
<FormattedMessage
145-
id="app.submission.evaluation.status.workInProgress"
146-
defaultMessage="The solution has not been evaluated yet."
147-
/>
148-
)}
149-
{evaluationStatus === 'failed' && (
150-
<FormattedMessage
151-
id="app.submission.evaluation.status.failed"
152-
defaultMessage="The solution does not meet the defined criteria."
153-
/>
154-
)}
155-
{evaluationStatus === 'evaluation-failed' && (
156-
<FormattedMessage
157-
id="app.submission.evaluation.status.systemFailiure"
158-
defaultMessage="Evaluation process failed and your submission could not have been evaluated. Please submit the solution once more. If you keep receiving errors please contact the administrator of this project."
159-
/>
160-
)}
161-
{evaluationStatus === 'missing-submission' && (
162-
<FormattedMessage
163-
id="app.submission.evaluation.status.solutionMissingSubmission"
164-
defaultMessage="The solution was not submitted for evaluation. This was probably caused by an error in the assignment configuration."
165-
/>
166-
)}
167-
</em>
154+
<SuccessOrFailureIcon success={accepted} />
168155
</td>
169156
</tr>
170157

171158
<tr>
172159
<td className="text-center">
173-
<Icon icon={['far', 'star']} />
160+
<Icon icon="stamp" />
174161
</td>
175162
<th className="text-nowrap">
176-
<FormattedMessage id="app.solution.scoredPoints" defaultMessage="Final score" />:
163+
<FormattedMessage id="app.solution.reviewed" defaultMessage="Reviewed" />:
177164
</th>
178-
<td
179-
className={classnames({
180-
'text-danger': actualPoints + bonusPoints <= 0,
181-
'text-success': actualPoints + bonusPoints > 0,
182-
})}>
183-
<b>
184-
{actualPoints || 0}
185-
{bonusPoints !== 0 ? (bonusPoints >= 0 ? '+' : '') + bonusPoints : ''} / {maxPoints}
186-
</b>
165+
<td>
166+
<SuccessOrFailureIcon success={reviewed} />
187167
</td>
188168
</tr>
189169
</tbody>
@@ -202,7 +182,8 @@ SolutionStatus.propTypes = {
202182
userId: PropTypes.string.isRequired,
203183
submittedBy: PropTypes.string,
204184
note: PropTypes.string,
205-
accepted: PropTypes.bool,
185+
accepted: PropTypes.bool.isRequired,
186+
reviewed: PropTypes.bool.isRequired,
206187
environment: PropTypes.object,
207188
maxPoints: PropTypes.number.isRequired,
208189
bonusPoints: PropTypes.number.isRequired,

src/components/buttons/AcceptSolution/AcceptSolution.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Icon from '../../icons';
66

77
const AcceptSolution = ({ accepted, acceptPending, accept, unaccept, shortLabel = false, bsSize = undefined }) =>
88
accepted === true ? (
9-
<Button bsStyle="info" bsSize={bsSize} onClick={unaccept} disabled={acceptPending}>
9+
<Button bsStyle="warning" bsSize={bsSize} onClick={unaccept} disabled={acceptPending}>
1010
<Icon icon="check-circle" gapRight />
1111
{shortLabel ? (
1212
<FormattedMessage id="app.acceptSolution.acceptedShort" defaultMessage="Revoke" />
@@ -15,7 +15,7 @@ const AcceptSolution = ({ accepted, acceptPending, accept, unaccept, shortLabel
1515
)}
1616
</Button>
1717
) : (
18-
<Button bsStyle="primary" bsSize={bsSize} onClick={accept} disabled={acceptPending}>
18+
<Button bsStyle="success" bsSize={bsSize} onClick={accept} disabled={acceptPending}>
1919
<Icon icon={['far', 'check-circle']} gapRight />
2020
{shortLabel ? (
2121
<FormattedMessage id="app.acceptSolution.notAcceptedShort" defaultMessage="Accept" />
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { FormattedMessage } from 'react-intl';
4+
import Button from '../../widgets/FlatButton';
5+
import Icon from '../../icons';
6+
7+
const ReviewSolution = ({ reviewed, reviewPending, setReviewed, unsetReviewed, bsSize = undefined }) =>
8+
reviewed === true ? (
9+
<Button bsStyle="info" bsSize={bsSize} onClick={unsetReviewed} disabled={reviewPending}>
10+
<Icon icon="eraser" gapRight />
11+
<FormattedMessage id="app.reviewedSolution.revoke" defaultMessage="Revoke Review" />
12+
</Button>
13+
) : (
14+
<Button bsStyle="primary" bsSize={bsSize} onClick={setReviewed} disabled={reviewPending}>
15+
<Icon icon="stamp" gapRight />
16+
<FormattedMessage id="app.reviewedSolution.set" defaultMessage="Review" />
17+
</Button>
18+
);
19+
20+
ReviewSolution.propTypes = {
21+
reviewed: PropTypes.bool.isRequired,
22+
reviewPending: PropTypes.bool.isRequired,
23+
setReviewed: PropTypes.func.isRequired,
24+
unsetReviewed: PropTypes.func.isRequired,
25+
bsSize: PropTypes.string,
26+
};
27+
28+
export default ReviewSolution;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import ReviewSolution from './ReviewSolution';
2+
export default ReviewSolution;

src/containers/AcceptSolutionContainer/AcceptedSolutionContainer.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import PropTypes from 'prop-types';
33
import { connect } from 'react-redux';
44

55
import AcceptSolution from '../../components/buttons/AcceptSolution';
6-
import { acceptSolution, unacceptSolution } from '../../redux/modules/solutions';
7-
import { isAccepted, isAcceptPending } from '../../redux/selectors/solutions';
6+
import { setSolutionFlag } from '../../redux/modules/solutions';
7+
import { isAccepted, isSetFlagPending } from '../../redux/selectors/solutions';
88

99
const AcceptSolutionContainer = ({ accepted, acceptPending, accept, unaccept, ...props }) => {
1010
return (
@@ -22,12 +22,12 @@ AcceptSolutionContainer.propTypes = {
2222

2323
const mapStateToProps = (state, { id }) => ({
2424
accepted: isAccepted(id)(state),
25-
acceptPending: isAcceptPending(id)(state),
25+
acceptPending: isSetFlagPending(id, 'accepted')(state),
2626
});
2727

2828
const mapDispatchToProps = (dispatch, { id }) => ({
29-
accept: () => dispatch(acceptSolution(id)),
30-
unaccept: () => dispatch(unacceptSolution(id)),
29+
accept: () => dispatch(setSolutionFlag(id, 'accepted', true)),
30+
unaccept: () => dispatch(setSolutionFlag(id, 'accepted', false)),
3131
});
3232

3333
export default connect(
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { connect } from 'react-redux';
4+
5+
import ReviewSolution from '../../components/buttons/ReviewSolution';
6+
import { setSolutionFlag } from '../../redux/modules/solutions';
7+
import { isReviewed, isSetFlagPending } from '../../redux/selectors/solutions';
8+
9+
const ReviewSolutionContainer = ({ reviewed, reviewPending, setReviewed, unsetReviewed, ...props }) => {
10+
return (
11+
<ReviewSolution
12+
reviewed={reviewed}
13+
reviewPending={reviewPending}
14+
setReviewed={setReviewed}
15+
unsetReviewed={unsetReviewed}
16+
{...props}
17+
/>
18+
);
19+
};
20+
21+
ReviewSolutionContainer.propTypes = {
22+
id: PropTypes.string.isRequired,
23+
reviewed: PropTypes.bool.isRequired,
24+
reviewPending: PropTypes.bool.isRequired,
25+
setReviewed: PropTypes.func.isRequired,
26+
unsetReviewed: PropTypes.func.isRequired,
27+
};
28+
29+
const mapStateToProps = (state, { id }) => ({
30+
reviewed: isReviewed(id)(state),
31+
reviewPending: isSetFlagPending(id, 'reviewed')(state),
32+
});
33+
34+
const mapDispatchToProps = (dispatch, { id }) => ({
35+
setReviewed: () => dispatch(setSolutionFlag(id, 'reviewed', true)),
36+
unsetReviewed: () => dispatch(setSolutionFlag(id, 'reviewed', false)),
37+
});
38+
39+
export default connect(
40+
mapStateToProps,
41+
mapDispatchToProps
42+
)(ReviewSolutionContainer);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import ReviewSolutionContainer from './ReviewSolutionContainer';
2+
export default ReviewSolutionContainer;

src/locales/cs.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,8 @@
10921092
"app.resultsArchiveInfoBox.description": "Detailní logy a výpisy",
10931093
"app.resultsArchiveInfoBox.title": "Archiv s výsledky",
10941094
"app.resultsTable.total": "Celkem",
1095+
"app.reviewedSolution.revoke": "Zrušit revizi",
1096+
"app.reviewedSolution.set": "Revidovat",
10951097
"app.roles.description.empoweredSupervisor": "Privilegovaná verze role vedoucího, která navíc přidává možnost vytvářet vlastní pipelines a použít tyto pipelines pro složitější konfigurace úloh.",
10961098
"app.roles.description.student": "Student je nejméně privilegovanou rolí, která má práva nahlížet pouze do skupin jejichž je členem a v těchto skupinách odevzdávat řešení úloh.",
10971099
"app.roles.description.superadmin": "Všemohoucí a vševidoucí uživatel, který může bez omezení provést cokoli v instancích, do kterých patří. Podobně jako root v linuxu nebo Q ve Startreku.",
@@ -1181,12 +1183,13 @@
11811183
"app.sisSupervisor.sisGroupsCreate": "Zakládání skupin asociovaných s kurzy ze SISu UK",
11821184
"app.sisSupervisor.sisGroupsCreateExplain": "SIS kurzy, které vyučujete v určitém semestru a které mají mapování do ReCodExu. Můžete vytvořit novou skupinu s vazbou na SIS, nebo svázat existující skupinu na tyto kurzy.",
11831185
"app.sisSupervisor.yearTerm": "Rok a semestr:",
1186+
"app.solution.acceptedAsFinal": "Akceptováno jako finální",
11841187
"app.solution.beforeFirstDeadline": "Před prvním termínem",
11851188
"app.solution.beforeSecondDeadline": "Před druhým termínem",
11861189
"app.solution.environment": "Cílový jazyk:",
11871190
"app.solution.environmentNotAllowedCannotResubmit": "Zadaná úloha již nepodporuje běhové prostředí, pod kterým bylo toto řešení odevzdáno. Řešení není možné znovu vyhodnotit.",
1188-
"app.solution.lastEvaluationStatus": "Stav posledního vyhodnocení",
11891191
"app.solution.note": "Poznámka:",
1192+
"app.solution.reviewed": "Revidováno",
11901193
"app.solution.scoredPoints": "Výsledné body",
11911194
"app.solution.submittedAt": "Odevzdáno",
11921195
"app.solution.title": "Řešení",
@@ -1200,6 +1203,7 @@
12001203
"app.solutionsTable.noSolutionsFound": "Zatím nebyla odevzdána žádná řešení.",
12011204
"app.solutionsTable.note": "Poznámka",
12021205
"app.solutionsTable.receivedPoints": "Body",
1206+
"app.solutionsTable.reviewedTooltip": "Řešení bylo posouzeno vedoucím.",
12031207
"app.solutionsTable.rowsCount": "Celkem záznamů: {count}",
12041208
"app.solutionsTable.solutionValidity": "Správnost",
12051209
"app.solutionsTable.submissionDate": "Datum odevzdání",

0 commit comments

Comments
 (0)